feat: basic database functionality
This commit is contained in:
112
lib/data/dao/statistic_dao.dart
Normal file
112
lib/data/dao/statistic_dao.dart
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:tallee/core/enums.dart';
|
||||||
|
import 'package:tallee/data/db/database.dart';
|
||||||
|
import 'package:tallee/data/db/tables/statistic_table.dart';
|
||||||
|
import 'package:tallee/data/models/statistic.dart';
|
||||||
|
|
||||||
|
part 'statistic_dao.g.dart';
|
||||||
|
|
||||||
|
@DriftAccessor(tables: [StatisticTable])
|
||||||
|
class StatisticDao extends DatabaseAccessor<AppDatabase>
|
||||||
|
with _$StatisticDaoMixin {
|
||||||
|
StatisticDao(super.db);
|
||||||
|
|
||||||
|
/* Create */
|
||||||
|
|
||||||
|
Future<bool> addStatistic({required Statistic statistic}) async {
|
||||||
|
await into(statisticTable).insert(
|
||||||
|
StatisticTableCompanion.insert(
|
||||||
|
id: statistic.id,
|
||||||
|
type: statistic.type.name,
|
||||||
|
timeframe: Value(statistic.timeframe?.name),
|
||||||
|
),
|
||||||
|
mode: InsertMode.insertOrReplace,
|
||||||
|
);
|
||||||
|
|
||||||
|
await db.statisticScopeDao.addStatisticScopes(
|
||||||
|
statisticId: statistic.id,
|
||||||
|
scopes: statistic.scopes,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (statistic.selectedGroups != null) {
|
||||||
|
await db.statisticGroupDao.addStatisticGroups(
|
||||||
|
statisticId: statistic.id,
|
||||||
|
groups: statistic.selectedGroups!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statistic.selectedGames != null) {
|
||||||
|
await db.statisticGameDao.addStatisticGames(
|
||||||
|
statisticId: statistic.id,
|
||||||
|
games: statistic.selectedGames!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read */
|
||||||
|
|
||||||
|
Future<Statistic?> getStatisticById(String statisticId) async {
|
||||||
|
final query = select(statisticTable);
|
||||||
|
final row = await query.getSingleOrNull();
|
||||||
|
if (row != null) {
|
||||||
|
final groups = await db.statisticGroupDao.getGroupsForStatistic(row.id);
|
||||||
|
final games = await db.statisticGameDao.getGamesForStatistic(row.id);
|
||||||
|
final scopes = await db.statisticScopeDao.getScopeForStatistic(row.id);
|
||||||
|
|
||||||
|
return Statistic(
|
||||||
|
type: StatisticType.values.firstWhere((type) => type.name == row.type),
|
||||||
|
scopes: scopes,
|
||||||
|
id: row.id,
|
||||||
|
timeframe: Timeframe.values.firstWhereOrNull(
|
||||||
|
(t) => t.name == row.timeframe,
|
||||||
|
),
|
||||||
|
selectedGroups: groups,
|
||||||
|
selectedGames: games,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves all statistics from the database, including their associated groups and games.
|
||||||
|
Future<List<Statistic>> getAllStatistics() async {
|
||||||
|
final query = select(statisticTable);
|
||||||
|
final rows = await query.get();
|
||||||
|
return Future.wait(
|
||||||
|
rows.map((row) async {
|
||||||
|
final groups = await db.statisticGroupDao.getGroupsForStatistic(row.id);
|
||||||
|
final games = await db.statisticGameDao.getGamesForStatistic(row.id);
|
||||||
|
|
||||||
|
return Statistic(
|
||||||
|
type: StatisticType.values.firstWhere(
|
||||||
|
(type) => type.name == row.type,
|
||||||
|
),
|
||||||
|
scopes: [],
|
||||||
|
id: row.id,
|
||||||
|
timeframe: Timeframe.values.firstWhereOrNull(
|
||||||
|
(t) => t.name == row.timeframe,
|
||||||
|
),
|
||||||
|
selectedGroups: groups,
|
||||||
|
selectedGames: games,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Delete */
|
||||||
|
|
||||||
|
Future<bool> deleteStatistic(String statisticId) async {
|
||||||
|
final rowsDeleted = await (delete(
|
||||||
|
statisticTable,
|
||||||
|
)..where((tbl) => tbl.id.equals(statisticId))).go();
|
||||||
|
|
||||||
|
return rowsDeleted > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> deleteAllStatistics() async {
|
||||||
|
final rowsDeleted = await delete(statisticTable).go();
|
||||||
|
return rowsDeleted > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
19
lib/data/dao/statistic_dao.g.dart
Normal file
19
lib/data/dao/statistic_dao.g.dart
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'statistic_dao.dart';
|
||||||
|
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
mixin _$StatisticDaoMixin on DatabaseAccessor<AppDatabase> {
|
||||||
|
$StatisticTableTable get statisticTable => attachedDatabase.statisticTable;
|
||||||
|
StatisticDaoManager get managers => StatisticDaoManager(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
class StatisticDaoManager {
|
||||||
|
final _$StatisticDaoMixin _db;
|
||||||
|
StatisticDaoManager(this._db);
|
||||||
|
$$StatisticTableTableTableManager get statisticTable =>
|
||||||
|
$$StatisticTableTableTableManager(
|
||||||
|
_db.attachedDatabase,
|
||||||
|
_db.statisticTable,
|
||||||
|
);
|
||||||
|
}
|
||||||
60
lib/data/dao/statistic_game_dao.dart
Normal file
60
lib/data/dao/statistic_game_dao.dart
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:tallee/core/enums.dart';
|
||||||
|
import 'package:tallee/data/db/database.dart';
|
||||||
|
import 'package:tallee/data/db/tables/statistic_game_table.dart';
|
||||||
|
import 'package:tallee/data/models/game.dart';
|
||||||
|
|
||||||
|
part 'statistic_game_dao.g.dart';
|
||||||
|
|
||||||
|
@DriftAccessor(tables: [StatisticGameTable])
|
||||||
|
class StatisticGameDao extends DatabaseAccessor<AppDatabase>
|
||||||
|
with _$StatisticGameDaoMixin {
|
||||||
|
StatisticGameDao(super.db);
|
||||||
|
|
||||||
|
/// Retrieves a list of games associated with a specific statistic.
|
||||||
|
Future<List<Game>> getGamesForStatistic(String statisticId) async {
|
||||||
|
final query = select(statisticGameTable).join([
|
||||||
|
innerJoin(gameTable, gameTable.id.equalsExp(statisticGameTable.gameId)),
|
||||||
|
])..where(statisticGameTable.statisticId.equals(statisticId));
|
||||||
|
|
||||||
|
final results = await query.map((row) => row.readTable(gameTable)).get();
|
||||||
|
return results
|
||||||
|
.map(
|
||||||
|
(result) => Game(
|
||||||
|
id: result.id,
|
||||||
|
name: result.name,
|
||||||
|
ruleset: Ruleset.values.firstWhere((e) => e.name == result.ruleset),
|
||||||
|
description: result.description,
|
||||||
|
color: GameColor.values.firstWhere((e) => e.name == result.color),
|
||||||
|
icon: result.icon,
|
||||||
|
createdAt: result.createdAt,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> addStatisticGames({
|
||||||
|
required String statisticId,
|
||||||
|
required List<Game> games,
|
||||||
|
}) {
|
||||||
|
final entries = games
|
||||||
|
.map(
|
||||||
|
(game) => StatisticGameTableCompanion.insert(
|
||||||
|
statisticId: statisticId,
|
||||||
|
gameId: game.id,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
return batch((batch) {
|
||||||
|
batch.insertAll(
|
||||||
|
statisticGameTable,
|
||||||
|
entries,
|
||||||
|
mode: InsertMode.insertOrReplace,
|
||||||
|
);
|
||||||
|
}).then((_) => true).catchError((error) {
|
||||||
|
print('Error adding statistic games: $error');
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
29
lib/data/dao/statistic_game_dao.g.dart
Normal file
29
lib/data/dao/statistic_game_dao.g.dart
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'statistic_game_dao.dart';
|
||||||
|
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
mixin _$StatisticGameDaoMixin on DatabaseAccessor<AppDatabase> {
|
||||||
|
$StatisticTableTable get statisticTable => attachedDatabase.statisticTable;
|
||||||
|
$GameTableTable get gameTable => attachedDatabase.gameTable;
|
||||||
|
$StatisticGameTableTable get statisticGameTable =>
|
||||||
|
attachedDatabase.statisticGameTable;
|
||||||
|
StatisticGameDaoManager get managers => StatisticGameDaoManager(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
class StatisticGameDaoManager {
|
||||||
|
final _$StatisticGameDaoMixin _db;
|
||||||
|
StatisticGameDaoManager(this._db);
|
||||||
|
$$StatisticTableTableTableManager get statisticTable =>
|
||||||
|
$$StatisticTableTableTableManager(
|
||||||
|
_db.attachedDatabase,
|
||||||
|
_db.statisticTable,
|
||||||
|
);
|
||||||
|
$$GameTableTableTableManager get gameTable =>
|
||||||
|
$$GameTableTableTableManager(_db.attachedDatabase, _db.gameTable);
|
||||||
|
$$StatisticGameTableTableTableManager get statisticGameTable =>
|
||||||
|
$$StatisticGameTableTableTableManager(
|
||||||
|
_db.attachedDatabase,
|
||||||
|
_db.statisticGameTable,
|
||||||
|
);
|
||||||
|
}
|
||||||
66
lib/data/dao/statistic_group_dao.dart
Normal file
66
lib/data/dao/statistic_group_dao.dart
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:tallee/data/db/database.dart';
|
||||||
|
import 'package:tallee/data/db/tables/group_table.dart';
|
||||||
|
import 'package:tallee/data/db/tables/statistic_group_table.dart';
|
||||||
|
import 'package:tallee/data/models/group.dart';
|
||||||
|
|
||||||
|
part 'statistic_group_dao.g.dart';
|
||||||
|
|
||||||
|
@DriftAccessor(tables: [StatisticGroupTable, GroupTable])
|
||||||
|
class StatisticGroupDao extends DatabaseAccessor<AppDatabase>
|
||||||
|
with _$StatisticGroupDaoMixin {
|
||||||
|
StatisticGroupDao(super.db);
|
||||||
|
|
||||||
|
/// Retrieves a list of groups associated with a specific statistic.
|
||||||
|
Future<List<Group>> getGroupsForStatistic(String statisticId) async {
|
||||||
|
final query = select(statisticGroupTable).join([
|
||||||
|
innerJoin(
|
||||||
|
groupTable,
|
||||||
|
groupTable.id.equalsExp(statisticGroupTable.groupId),
|
||||||
|
),
|
||||||
|
])..where(statisticGroupTable.statisticId.equals(statisticId));
|
||||||
|
|
||||||
|
final results = await query.map((row) => row.readTable(groupTable)).get();
|
||||||
|
final groups = await Future.wait(
|
||||||
|
results.map((result) async {
|
||||||
|
final groupMembers = await db.playerGroupDao.getPlayersOfGroup(
|
||||||
|
groupId: result.id,
|
||||||
|
);
|
||||||
|
return Group(
|
||||||
|
id: result.id,
|
||||||
|
createdAt: result.createdAt,
|
||||||
|
name: result.name,
|
||||||
|
description: result.description,
|
||||||
|
members: groupMembers,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> addStatisticGroups({
|
||||||
|
required String statisticId,
|
||||||
|
required List<Group> groups,
|
||||||
|
}) async {
|
||||||
|
final entries = groups
|
||||||
|
.map(
|
||||||
|
(group) => StatisticGroupTableCompanion.insert(
|
||||||
|
statisticId: statisticId,
|
||||||
|
groupId: group.id,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
return batch((batch) {
|
||||||
|
batch.insertAll(
|
||||||
|
statisticGroupTable,
|
||||||
|
entries,
|
||||||
|
mode: InsertMode.insertOrReplace,
|
||||||
|
);
|
||||||
|
}).then((_) => true).catchError((error) {
|
||||||
|
print('Error adding statistic groups: $error');
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
29
lib/data/dao/statistic_group_dao.g.dart
Normal file
29
lib/data/dao/statistic_group_dao.g.dart
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'statistic_group_dao.dart';
|
||||||
|
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
mixin _$StatisticGroupDaoMixin on DatabaseAccessor<AppDatabase> {
|
||||||
|
$StatisticTableTable get statisticTable => attachedDatabase.statisticTable;
|
||||||
|
$GroupTableTable get groupTable => attachedDatabase.groupTable;
|
||||||
|
$StatisticGroupTableTable get statisticGroupTable =>
|
||||||
|
attachedDatabase.statisticGroupTable;
|
||||||
|
StatisticGroupDaoManager get managers => StatisticGroupDaoManager(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
class StatisticGroupDaoManager {
|
||||||
|
final _$StatisticGroupDaoMixin _db;
|
||||||
|
StatisticGroupDaoManager(this._db);
|
||||||
|
$$StatisticTableTableTableManager get statisticTable =>
|
||||||
|
$$StatisticTableTableTableManager(
|
||||||
|
_db.attachedDatabase,
|
||||||
|
_db.statisticTable,
|
||||||
|
);
|
||||||
|
$$GroupTableTableTableManager get groupTable =>
|
||||||
|
$$GroupTableTableTableManager(_db.attachedDatabase, _db.groupTable);
|
||||||
|
$$StatisticGroupTableTableTableManager get statisticGroupTable =>
|
||||||
|
$$StatisticGroupTableTableTableManager(
|
||||||
|
_db.attachedDatabase,
|
||||||
|
_db.statisticGroupTable,
|
||||||
|
);
|
||||||
|
}
|
||||||
55
lib/data/dao/statistic_scope_dao.dart
Normal file
55
lib/data/dao/statistic_scope_dao.dart
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:tallee/core/enums.dart';
|
||||||
|
import 'package:tallee/data/db/database.dart';
|
||||||
|
import 'package:tallee/data/db/tables/statistic_scope_table.dart';
|
||||||
|
|
||||||
|
part 'statistic_scope_dao.g.dart';
|
||||||
|
|
||||||
|
@DriftAccessor(tables: [StatisticScopeTable])
|
||||||
|
class StatisticScopeDao extends DatabaseAccessor<AppDatabase>
|
||||||
|
with _$StatisticScopeDaoMixin {
|
||||||
|
StatisticScopeDao(super.db);
|
||||||
|
|
||||||
|
/// Retrieves a list of statistic scopes associated with a specific statistic ID.
|
||||||
|
Future<List<StatisticScope>> getScopeForStatistic(String statisticId) async {
|
||||||
|
final query = select(statisticScopeTable)
|
||||||
|
..where((tbl) => tbl.statisticId.equals(statisticId));
|
||||||
|
|
||||||
|
final results = await query.get();
|
||||||
|
return results
|
||||||
|
.map(
|
||||||
|
(result) => StatisticScope.values.firstWhere(
|
||||||
|
(e) => e.name == result.scope,
|
||||||
|
orElse: () => throw Exception(
|
||||||
|
'Invalid scope value: ${result.scope} for statistic ID: $statisticId',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> addStatisticScopes({
|
||||||
|
required String statisticId,
|
||||||
|
required List<StatisticScope> scopes,
|
||||||
|
}) async {
|
||||||
|
final entries = scopes
|
||||||
|
.map(
|
||||||
|
(scope) => StatisticScopeTableCompanion.insert(
|
||||||
|
statisticId: statisticId,
|
||||||
|
scope: scope.name,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
return batch((batch) {
|
||||||
|
batch.insertAll(
|
||||||
|
statisticScopeTable,
|
||||||
|
entries,
|
||||||
|
mode: InsertMode.insertOrReplace,
|
||||||
|
);
|
||||||
|
}).then((_) => true).catchError((error) {
|
||||||
|
print('Error adding statistic scopes: $error');
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
26
lib/data/dao/statistic_scope_dao.g.dart
Normal file
26
lib/data/dao/statistic_scope_dao.g.dart
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'statistic_scope_dao.dart';
|
||||||
|
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
mixin _$StatisticScopeDaoMixin on DatabaseAccessor<AppDatabase> {
|
||||||
|
$StatisticTableTable get statisticTable => attachedDatabase.statisticTable;
|
||||||
|
$StatisticScopeTableTable get statisticScopeTable =>
|
||||||
|
attachedDatabase.statisticScopeTable;
|
||||||
|
StatisticScopeDaoManager get managers => StatisticScopeDaoManager(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
class StatisticScopeDaoManager {
|
||||||
|
final _$StatisticScopeDaoMixin _db;
|
||||||
|
StatisticScopeDaoManager(this._db);
|
||||||
|
$$StatisticTableTableTableManager get statisticTable =>
|
||||||
|
$$StatisticTableTableTableManager(
|
||||||
|
_db.attachedDatabase,
|
||||||
|
_db.statisticTable,
|
||||||
|
);
|
||||||
|
$$StatisticScopeTableTableTableManager get statisticScopeTable =>
|
||||||
|
$$StatisticScopeTableTableTableManager(
|
||||||
|
_db.attachedDatabase,
|
||||||
|
_db.statisticScopeTable,
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -8,6 +8,10 @@ import 'package:tallee/data/dao/player_dao.dart';
|
|||||||
import 'package:tallee/data/dao/player_group_dao.dart';
|
import 'package:tallee/data/dao/player_group_dao.dart';
|
||||||
import 'package:tallee/data/dao/player_match_dao.dart';
|
import 'package:tallee/data/dao/player_match_dao.dart';
|
||||||
import 'package:tallee/data/dao/score_entry_dao.dart';
|
import 'package:tallee/data/dao/score_entry_dao.dart';
|
||||||
|
import 'package:tallee/data/dao/statistic_dao.dart';
|
||||||
|
import 'package:tallee/data/dao/statistic_game_dao.dart';
|
||||||
|
import 'package:tallee/data/dao/statistic_group_dao.dart';
|
||||||
|
import 'package:tallee/data/dao/statistic_scope_dao.dart';
|
||||||
import 'package:tallee/data/dao/team_dao.dart';
|
import 'package:tallee/data/dao/team_dao.dart';
|
||||||
import 'package:tallee/data/db/tables/game_table.dart';
|
import 'package:tallee/data/db/tables/game_table.dart';
|
||||||
import 'package:tallee/data/db/tables/group_table.dart';
|
import 'package:tallee/data/db/tables/group_table.dart';
|
||||||
@@ -16,6 +20,10 @@ import 'package:tallee/data/db/tables/player_group_table.dart';
|
|||||||
import 'package:tallee/data/db/tables/player_match_table.dart';
|
import 'package:tallee/data/db/tables/player_match_table.dart';
|
||||||
import 'package:tallee/data/db/tables/player_table.dart';
|
import 'package:tallee/data/db/tables/player_table.dart';
|
||||||
import 'package:tallee/data/db/tables/score_entry_table.dart';
|
import 'package:tallee/data/db/tables/score_entry_table.dart';
|
||||||
|
import 'package:tallee/data/db/tables/statistic_game_table.dart';
|
||||||
|
import 'package:tallee/data/db/tables/statistic_group_table.dart';
|
||||||
|
import 'package:tallee/data/db/tables/statistic_scope_table.dart';
|
||||||
|
import 'package:tallee/data/db/tables/statistic_table.dart';
|
||||||
import 'package:tallee/data/db/tables/team_table.dart';
|
import 'package:tallee/data/db/tables/team_table.dart';
|
||||||
|
|
||||||
part 'database.g.dart';
|
part 'database.g.dart';
|
||||||
@@ -30,6 +38,10 @@ part 'database.g.dart';
|
|||||||
GameTable,
|
GameTable,
|
||||||
TeamTable,
|
TeamTable,
|
||||||
ScoreEntryTable,
|
ScoreEntryTable,
|
||||||
|
StatisticTable,
|
||||||
|
StatisticScopeTable,
|
||||||
|
StatisticGameTable,
|
||||||
|
StatisticGroupTable,
|
||||||
],
|
],
|
||||||
daos: [
|
daos: [
|
||||||
PlayerDao,
|
PlayerDao,
|
||||||
@@ -40,6 +52,10 @@ part 'database.g.dart';
|
|||||||
GameDao,
|
GameDao,
|
||||||
ScoreEntryDao,
|
ScoreEntryDao,
|
||||||
TeamDao,
|
TeamDao,
|
||||||
|
StatisticDao,
|
||||||
|
StatisticScopeDao,
|
||||||
|
StatisticGameDao,
|
||||||
|
StatisticGroupDao,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
class AppDatabase extends _$AppDatabase {
|
class AppDatabase extends _$AppDatabase {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
13
lib/data/db/tables/statistic_game_table.dart
Normal file
13
lib/data/db/tables/statistic_game_table.dart
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:tallee/data/db/tables/game_table.dart';
|
||||||
|
import 'package:tallee/data/db/tables/statistic_table.dart';
|
||||||
|
|
||||||
|
class StatisticGameTable extends Table {
|
||||||
|
TextColumn get statisticId =>
|
||||||
|
text().references(StatisticTable, #id, onDelete: KeyAction.cascade)();
|
||||||
|
TextColumn get gameId =>
|
||||||
|
text().references(GameTable, #id, onDelete: KeyAction.cascade)();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<Column<Object>> get primaryKey => {statisticId, gameId};
|
||||||
|
}
|
||||||
13
lib/data/db/tables/statistic_group_table.dart
Normal file
13
lib/data/db/tables/statistic_group_table.dart
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:tallee/data/db/tables/group_table.dart';
|
||||||
|
import 'package:tallee/data/db/tables/statistic_table.dart';
|
||||||
|
|
||||||
|
class StatisticGroupTable extends Table {
|
||||||
|
TextColumn get statisticId =>
|
||||||
|
text().references(StatisticTable, #id, onDelete: KeyAction.cascade)();
|
||||||
|
TextColumn get groupId =>
|
||||||
|
text().references(GroupTable, #id, onDelete: KeyAction.cascade)();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<Column<Object>> get primaryKey => {statisticId, groupId};
|
||||||
|
}
|
||||||
11
lib/data/db/tables/statistic_scope_table.dart
Normal file
11
lib/data/db/tables/statistic_scope_table.dart
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:tallee/data/db/tables/statistic_table.dart';
|
||||||
|
|
||||||
|
class StatisticScopeTable extends Table {
|
||||||
|
TextColumn get statisticId =>
|
||||||
|
text().references(StatisticTable, #id, onDelete: KeyAction.cascade)();
|
||||||
|
TextColumn get scope => text()();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<Column<Object>> get primaryKey => {statisticId, scope};
|
||||||
|
}
|
||||||
10
lib/data/db/tables/statistic_table.dart
Normal file
10
lib/data/db/tables/statistic_table.dart
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
class StatisticTable extends Table {
|
||||||
|
TextColumn get id => text()();
|
||||||
|
TextColumn get type => text()();
|
||||||
|
TextColumn get timeframe => text().nullable()();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<Column<Object>> get primaryKey => {id};
|
||||||
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
import 'package:tallee/core/enums.dart';
|
import 'package:tallee/core/enums.dart';
|
||||||
import 'package:tallee/data/models/game.dart';
|
import 'package:tallee/data/models/game.dart';
|
||||||
import 'package:tallee/data/models/group.dart';
|
import 'package:tallee/data/models/group.dart';
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
class Statistic {
|
class Statistic {
|
||||||
|
final String id;
|
||||||
final StatisticType type;
|
final StatisticType type;
|
||||||
final List<StatisticScope> scopes;
|
final List<StatisticScope> scopes;
|
||||||
final Timeframe? timeframe;
|
final Timeframe? timeframe;
|
||||||
@@ -12,8 +14,14 @@ class Statistic {
|
|||||||
Statistic({
|
Statistic({
|
||||||
required this.type,
|
required this.type,
|
||||||
required this.scopes,
|
required this.scopes,
|
||||||
|
String? id,
|
||||||
this.timeframe,
|
this.timeframe,
|
||||||
this.selectedGroups,
|
this.selectedGroups,
|
||||||
this.selectedGames,
|
this.selectedGames,
|
||||||
});
|
}) : id = id ?? const Uuid().v4();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'Statistic(id: $id, type: $type, scopes: $scopes, timeframe: $timeframe, selectedGroups: $selectedGroups, selectedGames: $selectedGames)';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -571,8 +571,8 @@ class _CreateStatisticViewState extends State<CreateStatisticView> {
|
|||||||
selectedGroups: selectedGroups,
|
selectedGroups: selectedGroups,
|
||||||
selectedGames: selectedGames,
|
selectedGames: selectedGames,
|
||||||
);
|
);
|
||||||
// final db = Provider.of<AppDatabase>(context, listen: false);
|
final db = Provider.of<AppDatabase>(context, listen: false);
|
||||||
// db.statisticDao.addStatistic(newStatistic);
|
db.statisticDao.addStatistic(statistic: newStatistic);
|
||||||
Navigator.of(context).pop(newStatistic);
|
Navigator.of(context).pop(newStatistic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,13 +5,12 @@ import 'package:tallee/core/constants.dart';
|
|||||||
import 'package:tallee/data/db/database.dart';
|
import 'package:tallee/data/db/database.dart';
|
||||||
import 'package:tallee/data/models/match.dart';
|
import 'package:tallee/data/models/match.dart';
|
||||||
import 'package:tallee/data/models/player.dart';
|
import 'package:tallee/data/models/player.dart';
|
||||||
|
import 'package:tallee/data/models/statistic.dart';
|
||||||
import 'package:tallee/l10n/generated/app_localizations.dart';
|
import 'package:tallee/l10n/generated/app_localizations.dart';
|
||||||
import 'package:tallee/presentation/views/main_menu/statistics_view/create_statistic_view.dart';
|
import 'package:tallee/presentation/views/main_menu/statistics_view/create_statistic_view.dart';
|
||||||
import 'package:tallee/presentation/widgets/app_skeleton.dart';
|
import 'package:tallee/presentation/widgets/app_skeleton.dart';
|
||||||
import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart';
|
import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart';
|
||||||
import 'package:tallee/presentation/widgets/tiles/quick_info_tile.dart';
|
import 'package:tallee/presentation/widgets/tiles/info_tile.dart';
|
||||||
import 'package:tallee/presentation/widgets/tiles/statistics_tile.dart';
|
|
||||||
import 'package:tallee/presentation/widgets/top_centered_message.dart';
|
|
||||||
|
|
||||||
class StatisticsView extends StatefulWidget {
|
class StatisticsView extends StatefulWidget {
|
||||||
/// A view that displays player statistics
|
/// A view that displays player statistics
|
||||||
@@ -38,11 +37,20 @@ class _StatisticsViewState extends State<StatisticsView> {
|
|||||||
1,
|
1,
|
||||||
));
|
));
|
||||||
bool isLoading = true;
|
bool isLoading = true;
|
||||||
|
List<Widget> statisticTiles = List.generate(
|
||||||
|
4,
|
||||||
|
(_) => const InfoTile(
|
||||||
|
icon: Icons.sports_score,
|
||||||
|
title: 'Skeleton Statistic',
|
||||||
|
width: double.infinity,
|
||||||
|
content: Text('Skeleton content'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
loadStatisticData();
|
getStatisticTiles(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -51,7 +59,8 @@ class _StatisticsViewState extends State<StatisticsView> {
|
|||||||
return LayoutBuilder(
|
return LayoutBuilder(
|
||||||
builder: (BuildContext context, BoxConstraints constraints) {
|
builder: (BuildContext context, BoxConstraints constraints) {
|
||||||
return Stack(
|
return Stack(
|
||||||
alignment: AlignmentDirectional.center,
|
alignment: AlignmentDirectional.bottomCenter,
|
||||||
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
child: AppSkeleton(
|
child: AppSkeleton(
|
||||||
@@ -62,73 +71,7 @@ class _StatisticsViewState extends State<StatisticsView> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [...statisticTiles],
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
QuickInfoTile(
|
|
||||||
width: constraints.maxWidth * 0.45,
|
|
||||||
height: constraints.maxHeight * 0.13,
|
|
||||||
title: loc.matches,
|
|
||||||
icon: Icons.groups_rounded,
|
|
||||||
value: matchCount,
|
|
||||||
),
|
|
||||||
SizedBox(width: constraints.maxWidth * 0.05),
|
|
||||||
QuickInfoTile(
|
|
||||||
width: constraints.maxWidth * 0.45,
|
|
||||||
height: constraints.maxHeight * 0.13,
|
|
||||||
title: loc.groups,
|
|
||||||
icon: Icons.groups_rounded,
|
|
||||||
value: groupCount,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
SizedBox(height: constraints.maxHeight * 0.02),
|
|
||||||
Visibility(
|
|
||||||
visible:
|
|
||||||
winCounts.isEmpty &&
|
|
||||||
matchCounts.isEmpty &&
|
|
||||||
winRates.isEmpty,
|
|
||||||
replacement: Column(
|
|
||||||
children: [
|
|
||||||
StatisticsTile(
|
|
||||||
icon: Icons.sports_score,
|
|
||||||
title: loc.wins,
|
|
||||||
width: constraints.maxWidth * 0.95,
|
|
||||||
values: winCounts,
|
|
||||||
itemCount: 3,
|
|
||||||
barColor: Colors.green,
|
|
||||||
),
|
|
||||||
SizedBox(height: constraints.maxHeight * 0.02),
|
|
||||||
StatisticsTile(
|
|
||||||
icon: Icons.percent,
|
|
||||||
title: loc.winrate,
|
|
||||||
width: constraints.maxWidth * 0.95,
|
|
||||||
values: winRates,
|
|
||||||
itemCount: 5,
|
|
||||||
barColor: Colors.orange[700]!,
|
|
||||||
),
|
|
||||||
SizedBox(height: constraints.maxHeight * 0.02),
|
|
||||||
StatisticsTile(
|
|
||||||
icon: Icons.casino,
|
|
||||||
title: loc.amount_of_matches,
|
|
||||||
width: constraints.maxWidth * 0.95,
|
|
||||||
values: matchCounts,
|
|
||||||
itemCount: 10,
|
|
||||||
barColor: Colors.blue,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: TopCenteredMessage(
|
|
||||||
icon: Icons.info,
|
|
||||||
title: loc.info,
|
|
||||||
message: AppLocalizations.of(
|
|
||||||
context,
|
|
||||||
).no_statistics_available,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: MediaQuery.paddingOf(context).bottom),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -138,8 +81,8 @@ class _StatisticsViewState extends State<StatisticsView> {
|
|||||||
child: MainMenuButton(
|
child: MainMenuButton(
|
||||||
text: loc.create_statistic,
|
text: loc.create_statistic,
|
||||||
icon: Icons.bar_chart,
|
icon: Icons.bar_chart,
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
Navigator.push(
|
Statistic newStatistic = await Navigator.push(
|
||||||
context,
|
context,
|
||||||
adaptivePageRoute(
|
adaptivePageRoute(
|
||||||
builder: (context) => CreateStatisticView(
|
builder: (context) => CreateStatisticView(
|
||||||
@@ -147,6 +90,18 @@ class _StatisticsViewState extends State<StatisticsView> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
final newTile = InfoTile(
|
||||||
|
icon: Icons.sports_score,
|
||||||
|
title: newStatistic.type.name,
|
||||||
|
width: MediaQuery.sizeOf(context).width * 0.95,
|
||||||
|
content: Text(
|
||||||
|
'${newStatistic.id}\n${newStatistic.scopes}\n${newStatistic.type}\n${newStatistic.timeframe}\n${newStatistic.selectedGroups}\n${newStatistic.selectedGames}\n',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
statisticTiles.add(newTile);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -196,6 +151,30 @@ class _StatisticsViewState extends State<StatisticsView> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> getStatisticTiles(BuildContext context) async {
|
||||||
|
isLoading = true;
|
||||||
|
final db = Provider.of<AppDatabase>(context, listen: false);
|
||||||
|
final statistics = await db.statisticDao.getAllStatistics();
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
statisticTiles = [];
|
||||||
|
for (var statistic in statistics) {
|
||||||
|
statisticTiles.add(
|
||||||
|
InfoTile(
|
||||||
|
icon: Icons.sports_score,
|
||||||
|
title: statistic.type.name,
|
||||||
|
width: MediaQuery.sizeOf(context).width * 0.95,
|
||||||
|
content: Text(statistic.toString()),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
statisticTiles.add(
|
||||||
|
SizedBox(height: MediaQuery.sizeOf(context).height * 0.02),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
isLoading = false;
|
||||||
|
}
|
||||||
|
|
||||||
/// 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<(Player, int)> _calculateWinsForAllPlayers({
|
List<(Player, int)> _calculateWinsForAllPlayers({
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
name: tallee
|
name: tallee
|
||||||
description: "Tracking App for Card Games"
|
description: "Tracking App for Card Games"
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
version: 0.0.33+273
|
version: 0.0.33+274
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.8.1
|
sdk: ^3.8.1
|
||||||
|
|||||||
124
test/db_tests/statistics/statistic_test.dart
Normal file
124
test/db_tests/statistics/statistic_test.dart
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
import 'dart:core';
|
||||||
|
|
||||||
|
import 'package:clock/clock.dart';
|
||||||
|
import 'package:drift/drift.dart' hide isNotNull;
|
||||||
|
import 'package:drift/native.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:tallee/core/enums.dart';
|
||||||
|
import 'package:tallee/data/db/database.dart';
|
||||||
|
import 'package:tallee/data/models/game.dart';
|
||||||
|
import 'package:tallee/data/models/group.dart';
|
||||||
|
import 'package:tallee/data/models/match.dart';
|
||||||
|
import 'package:tallee/data/models/player.dart';
|
||||||
|
import 'package:tallee/data/models/score_entry.dart';
|
||||||
|
import 'package:tallee/data/models/statistic.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late AppDatabase database;
|
||||||
|
late Player testPlayer1;
|
||||||
|
late Player testPlayer2;
|
||||||
|
late Player testPlayer3;
|
||||||
|
late Player testPlayer4;
|
||||||
|
late Player testPlayer5;
|
||||||
|
late Group testGroup1;
|
||||||
|
late Group testGroup2;
|
||||||
|
late Game testGame;
|
||||||
|
late Match testMatch1;
|
||||||
|
late Match testMatch2;
|
||||||
|
late Match testMatchOnlyPlayers;
|
||||||
|
late Match testMatchOnlyGroup;
|
||||||
|
final fixedDate = DateTime(2025, 19, 11, 00, 11, 23);
|
||||||
|
final fakeClock = Clock(() => fixedDate);
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
database = AppDatabase(
|
||||||
|
DatabaseConnection(
|
||||||
|
NativeDatabase.memory(),
|
||||||
|
// Recommended for widget tests to avoid test errors.
|
||||||
|
closeStreamsSynchronously: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
withClock(fakeClock, () {
|
||||||
|
testPlayer1 = Player(name: 'Alice');
|
||||||
|
testPlayer2 = Player(name: 'Bob');
|
||||||
|
testPlayer3 = Player(name: 'Charlie');
|
||||||
|
testPlayer4 = Player(name: 'Diana');
|
||||||
|
testPlayer5 = Player(name: 'Eve');
|
||||||
|
testGroup1 = Group(
|
||||||
|
name: 'Test Group 1',
|
||||||
|
description: '',
|
||||||
|
members: [testPlayer1, testPlayer2, testPlayer3],
|
||||||
|
);
|
||||||
|
testGroup2 = Group(
|
||||||
|
name: 'Test Group 2',
|
||||||
|
description: '',
|
||||||
|
members: [testPlayer4, testPlayer5],
|
||||||
|
);
|
||||||
|
testGame = Game(
|
||||||
|
name: 'Test Game',
|
||||||
|
ruleset: Ruleset.singleWinner,
|
||||||
|
description: 'A test game',
|
||||||
|
color: GameColor.blue,
|
||||||
|
icon: '',
|
||||||
|
);
|
||||||
|
testMatch1 = Match(
|
||||||
|
name: 'First Test Match',
|
||||||
|
game: testGame,
|
||||||
|
group: testGroup1,
|
||||||
|
players: [testPlayer4, testPlayer5],
|
||||||
|
scores: {testPlayer4.id: ScoreEntry(score: 1)},
|
||||||
|
);
|
||||||
|
testMatch2 = Match(
|
||||||
|
name: 'Second Test Match',
|
||||||
|
game: testGame,
|
||||||
|
group: testGroup2,
|
||||||
|
players: [testPlayer1, testPlayer2, testPlayer3],
|
||||||
|
);
|
||||||
|
testMatchOnlyPlayers = Match(
|
||||||
|
name: 'Test Match with Players',
|
||||||
|
game: testGame,
|
||||||
|
players: [testPlayer1, testPlayer2, testPlayer3],
|
||||||
|
);
|
||||||
|
testMatchOnlyGroup = Match(
|
||||||
|
name: 'Test Match with Group',
|
||||||
|
game: testGame,
|
||||||
|
group: testGroup2,
|
||||||
|
players: testGroup2.members,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
await database.playerDao.addPlayersAsList(
|
||||||
|
players: [
|
||||||
|
testPlayer1,
|
||||||
|
testPlayer2,
|
||||||
|
testPlayer3,
|
||||||
|
testPlayer4,
|
||||||
|
testPlayer5,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
await database.groupDao.addGroupsAsList(groups: [testGroup1, testGroup2]);
|
||||||
|
await database.gameDao.addGame(game: testGame);
|
||||||
|
});
|
||||||
|
tearDown(() async {
|
||||||
|
await database.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Adding/fetching statistic works correclty', () async {
|
||||||
|
final statistic = Statistic(
|
||||||
|
type: StatisticType.averageScore,
|
||||||
|
scopes: [StatisticScope.allPlayers, StatisticScope.selectedGames],
|
||||||
|
timeframe: Timeframe.allTime,
|
||||||
|
selectedGames: [testGame],
|
||||||
|
selectedGroups: [testGroup1],
|
||||||
|
);
|
||||||
|
|
||||||
|
final added = await database.statisticDao.addStatistic(
|
||||||
|
statistic: statistic,
|
||||||
|
);
|
||||||
|
expect(added, isTrue);
|
||||||
|
|
||||||
|
final fetched = await database.statisticDao.getStatisticById(statistic.id);
|
||||||
|
expect(fetched, isNotNull);
|
||||||
|
expect(fetched!.type, statistic.type);
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user