Neue Datenbank Struktur #156

Open
gelbeinhalb wants to merge 88 commits from feature/88-neue-datenbank-struktur into development
15 changed files with 94 additions and 40 deletions
Showing only changes of commit b0cb385756 - Show all commits

View File

@@ -29,24 +29,27 @@ enum ImportResult {
/// - [ExportResult.unknownException]: An exception occurred during export.
enum ExportResult { success, canceled, unknownException }
/// Different rulesets available for matches
/// - [Ruleset.singleWinner]: The match is won by a single player
/// - [Ruleset.singleLoser]: The match is lost by a single player
/// - [Ruleset.mostPoints]: The player with the most points wins.
/// - [Ruleset.leastPoints]: The player with the fewest points wins.
enum Ruleset { singleWinner, singleLoser, mostPoints, leastPoints }
/// Different rulesets available for games
/// - [Ruleset.highestScore]: The player with the highest score wins.
/// - [Ruleset.lowestScore]: The player with the lowest score wins.
/// - [Ruleset.singleWinner]: The match is won by a single player.
/// - [Ruleset.singleLoser]: The match has a single loser.
/// - [Ruleset.multipleWinners]: Multiple players can be winners.
enum Ruleset { highestScore, lowestScore, singleWinner, singleLoser, multipleWinners }
/// Translates a [Ruleset] enum value to its corresponding localized string.
String translateRulesetToString(Ruleset ruleset, BuildContext context) {
final loc = AppLocalizations.of(context);
switch (ruleset) {
case Ruleset.highestScore:
return loc.highest_score;
case Ruleset.lowestScore:
return loc.lowest_score;
case Ruleset.singleWinner:
return loc.single_winner;
case Ruleset.singleLoser:
return loc.single_loser;
case Ruleset.mostPoints:
return loc.most_points;
case Ruleset.leastPoints:
return loc.least_points;
case Ruleset.multipleWinners:
return loc.multiple_winners;
}
}

View File

@@ -2,6 +2,7 @@ import 'package:drift/drift.dart';
import 'package:game_tracker/data/db/database.dart';
import 'package:game_tracker/data/db/tables/game_table.dart';
import 'package:game_tracker/data/dto/game.dart';
import 'package:game_tracker/core/enums.dart';
part 'game_dao.g.dart';
@@ -18,7 +19,7 @@ class GameDao extends DatabaseAccessor<AppDatabase> with _$GameDaoMixin {
(row) => Game(
id: row.id,
name: row.name,
ruleset: row.ruleset,
ruleset: Ruleset.values.firstWhere((e) => e.name == row.ruleset),
description: row.description,
color: row.color,
icon: row.icon,
@@ -35,7 +36,7 @@ class GameDao extends DatabaseAccessor<AppDatabase> with _$GameDaoMixin {
return Game(
id: result.id,
name: result.name,
ruleset: result.ruleset,
ruleset: Ruleset.values.firstWhere((e) => e.name == result.ruleset),
flixcoo marked this conversation as resolved
Review

Funktioniert das tatsächlich? Ist das getestet, dass dann auch ein ruleset (color) gefunden wird wenn du den enum speicherst?

Funktioniert das tatsächlich? Ist das getestet, dass dann auch ein ruleset (color) gefunden wird wenn du den enum speicherst?
Review

Ja glaube das hat funktioniert

Ja glaube das hat funktioniert
description: result.description,
color: result.color,
icon: result.icon,
@@ -52,7 +53,7 @@ class GameDao extends DatabaseAccessor<AppDatabase> with _$GameDaoMixin {
GameTableCompanion.insert(
id: game.id,
name: game.name,
ruleset: game.ruleset ?? '',
ruleset: game.ruleset.name,
description: game.description,
color: game.color,
icon: Value(game.icon),
@@ -78,7 +79,7 @@ class GameDao extends DatabaseAccessor<AppDatabase> with _$GameDaoMixin {
(game) => GameTableCompanion.insert(
id: game.id,
name: game.name,
ruleset: game.ruleset ?? '',
ruleset: game.ruleset.name,
description: game.description,
color: game.color,
icon: Value(game.icon),
@@ -122,10 +123,10 @@ class GameDao extends DatabaseAccessor<AppDatabase> with _$GameDaoMixin {
/// Updates the ruleset of the game with the given [gameId].
Future<void> updateGameRuleset({
required String gameId,
required String newRuleset,
required Ruleset newRuleset,
}) async {
await (update(gameTable)..where((g) => g.id.equals(gameId))).write(
GameTableCompanion(ruleset: Value(newRuleset)),
GameTableCompanion(ruleset: Value(newRuleset.name)),
);
}

View File

@@ -127,7 +127,7 @@ class MatchDao extends DatabaseAccessor<AppDatabase> with _$MatchDaoMixin {
(game) => GameTableCompanion.insert(
id: game.id,
name: game.name,
ruleset: game.ruleset ?? '',
ruleset: game.ruleset.name,
description: game.description,
color: game.color,
icon: Value(game.icon),

View File

@@ -1,11 +1,12 @@
import 'package:clock/clock.dart';
import 'package:uuid/uuid.dart';
import 'package:game_tracker/core/enums.dart';
class Game {
final String id;
final DateTime createdAt;
final String name;
gelbeinhalb marked this conversation as resolved
Review

Ruleset sollte nicht optional, weil das immer gebraucht wird
Ruleset als enum

Ruleset sollte nicht optional, weil das immer gebraucht wird Ruleset als enum
final String? ruleset;
final Ruleset ruleset;
gelbeinhalb marked this conversation as resolved Outdated

Description als leeren string setzen, wenn nicht übergeben

Description als leeren string setzen, wenn nicht übergeben

warum findest du leeren string besser als nullable?

warum findest du leeren string besser als nullable?

Finde null sollte man nur dann verwenden, wenns halt nicht anders geht, wie z.B. bei objekten

Finde null sollte man nur dann verwenden, wenns halt nicht anders geht, wie z.B. bei objekten
final String description;
gelbeinhalb marked this conversation as resolved Outdated

Hier lieber enum, gerne auch mit einem Color.none

Hier lieber enum, gerne auch mit einem `Color.none`

warum aber nicht hex codes?

warum aber nicht hex codes?

oder willst du die farben vorgeben?

oder willst du die farben vorgeben?

Ja man könnte auch hexcodes machen. Würde die Farben so oder so vorgeben unabhängig davon wie man sie speichert

Ja man könnte auch hexcodes machen. Würde die Farben so oder so vorgeben unabhängig davon wie man sie speichert

würde enums maybe machen, weil man sonst im export einfach den hex code ändern könnte. Also idk ob das juckt aber 🤷‍♂️

würde enums maybe machen, weil man sonst im export einfach den hex code ändern könnte. Also idk ob das juckt aber 🤷‍♂️
final String color;
gelbeinhalb marked this conversation as resolved Outdated

Hier auch mit enum arbeiten?

Hier auch mit enum arbeiten?
final String? icon;
@@ -14,7 +15,7 @@ class Game {
String? id,
DateTime? createdAt,
required this.name,
this.ruleset,
required this.ruleset,
required this.description,
Review

Description sollte nicht obligatorisch sein, sondern optional. Wenn nicht gesetzt soll es ein leerer String werden.

Description sollte nicht obligatorisch sein, sondern optional. Wenn nicht gesetzt soll es ein leerer String werden.
Review

Dachte man soll den als leeren String selber setzen müssen

Dachte man soll den als leeren String selber setzen müssen
Review

würde das lieber so machen, weil man ja sonst immer unnötig die description setzten muss

würde das lieber so machen, weil man ja sonst immer unnötig die description setzten muss
Review

finde auch optional

finde auch optional
required this.color,
this.icon,
@@ -31,7 +32,7 @@ class Game {
: id = json['id'],
createdAt = DateTime.parse(json['createdAt']),
name = json['name'],
ruleset = json['ruleset'],
ruleset = Ruleset.values.firstWhere((e) => e.name == json['ruleset']),
description = json['description'],
color = json['color'],
icon = json['icon'];
@@ -41,7 +42,7 @@ class Game {
'id': id,
'createdAt': createdAt.toIso8601String(),
'name': name,
'ruleset': ruleset,
'ruleset': ruleset.name,
'description': description,
'color': color,
'icon': icon,

View File

@@ -82,6 +82,9 @@
"settings": "Einstellungen",
"single_loser": "Ein:e Verlierer:in",
"single_winner": "Ein:e Gewinner:in",
"highest_score": "Höchste Punkte",
"lowest_score": "Niedrigste Punkte",
"multiple_winners": "Mehrere Gewinner:innen",
"statistics": "Statistiken",
"stats": "Statistiken",
"successfully_added_player": "Spieler:in {playerName} erfolgreich hinzugefügt",

View File

@@ -380,6 +380,9 @@
"settings": "Settings",
"single_loser": "Single Loser",
"single_winner": "Single Winner",
"highest_score": "Highest Score",
"lowest_score": "Lowest Score",
"multiple_winners": "Multiple Winners",
"statistics": "Statistics",
"stats": "Stats",
"successfully_added_player": "Successfully added player {playerName}",

View File

@@ -590,6 +590,24 @@ abstract class AppLocalizations {
/// **'Single Winner'**
String get single_winner;
/// No description provided for @highest_score.
///
/// In en, this message translates to:
/// **'Highest Score'**
String get highest_score;
/// No description provided for @lowest_score.
///
/// In en, this message translates to:
/// **'Lowest Score'**
String get lowest_score;
/// No description provided for @multiple_winners.
///
/// In en, this message translates to:
/// **'Multiple Winners'**
String get multiple_winners;
/// Statistics tab label
///
/// In en, this message translates to:

View File

@@ -266,6 +266,15 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get single_winner => 'Ein:e Gewinner:in';
@override
String get highest_score => 'Höchste Punkte';
@override
String get lowest_score => 'Niedrigste Punkte';
@override
String get multiple_winners => 'Mehrere Gewinner:innen';
@override
String get statistics => 'Statistiken';

View File

@@ -266,6 +266,15 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get single_winner => 'Single Winner';
@override
String get highest_score => 'Highest Score';
@override
String get lowest_score => 'Lowest Score';
@override
String get multiple_winners => 'Multiple Winners';
@override
String get statistics => 'Statistics';

View File

@@ -100,7 +100,7 @@ class _CreateMatchViewState extends State<CreateMatchView> {
}
List<(String, String, Ruleset)> games = [
('Example Game 1', 'This is a description', Ruleset.leastPoints),
('Example Game 1', 'This is a description', Ruleset.lowestScore),
('Example Game 2', '', Ruleset.singleWinner),
];
@@ -201,7 +201,7 @@ class _CreateMatchViewState extends State<CreateMatchView> {
gameToUse = Game(
name: selectedGame.$1,
description: selectedGame.$2,
ruleset: selectedGame.$3.name,
ruleset: selectedGame.$3,
color: '0xFF000000',
);
} else {
@@ -210,7 +210,7 @@ class _CreateMatchViewState extends State<CreateMatchView> {
gameToUse = Game(
name: selectedGame.$1,
description: selectedGame.$2,
ruleset: selectedGame.$3.name,
ruleset: selectedGame.$3,
color: '0xFF000000',
);
}

View File

@@ -4,6 +4,7 @@ import 'package:drift/native.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:game_tracker/data/db/database.dart';
import 'package:game_tracker/data/dto/game.dart';
import 'package:game_tracker/core/enums.dart';
void main() {
late AppDatabase database;
@@ -24,7 +25,7 @@ void main() {
withClock(fakeClock, () {
testGame1 = Game(
name: 'Chess',
ruleset: 'winner.single',
ruleset: Ruleset.singleWinner,
description: 'A classic strategy game',
color: '0xFF0000FF',
icon: 'chess_icon',
@@ -32,14 +33,15 @@ void main() {
testGame2 = Game(
id: 'game2',
name: 'Poker',
ruleset: 'Texas Hold\'em rules',
description: 'winner.multiple',
ruleset: Ruleset.multipleWinners,
description: 'Card game with multiple winners',
color: '0xFFFF0000',
icon: 'poker_icon',
);
testGame3 = Game(
id: 'game3',
name: 'Monopoly',
ruleset: Ruleset.highestScore,
description: 'A board game about real estate',
color: '0xFF000000',
);
@@ -131,7 +133,7 @@ void main() {
// Verifies that a game with null optional fields can be added and retrieved.
test('addGame handles game with null optional fields', () async {
final gameWithNulls = Game(name: 'Simple Game', description: 'A simple game', color: '0xFF000000');
final gameWithNulls = Game(name: 'Simple Game', ruleset: Ruleset.lowestScore, description: 'A simple game', color: '0xFF000000');
final result = await database.gameDao.addGame(game: gameWithNulls);
expect(result, true);
@@ -269,13 +271,13 @@ void main() {
await database.gameDao.updateGameRuleset(
gameId: testGame1.id,
newRuleset: 'New ruleset for chess',
newRuleset: Ruleset.highestScore,
);
final updatedGame = await database.gameDao.getGameById(
gameId: testGame1.id,
);
expect(updatedGame.ruleset, 'New ruleset for chess');
expect(updatedGame.ruleset, Ruleset.highestScore);
expect(updatedGame.name, testGame1.name);
});
@@ -283,7 +285,7 @@ void main() {
test('updateGameRuleset does nothing for non-existent game', () async {
await database.gameDao.updateGameRuleset(
gameId: 'non-existent-id',
newRuleset: 'New Ruleset',
newRuleset: Ruleset.lowestScore,
);
final allGames = await database.gameDao.getAllGames();
@@ -455,6 +457,7 @@ void main() {
test('Game with special characters in name is stored correctly', () async {
final specialGame = Game(
name: 'Game\'s & "Special" <Name>',
ruleset: Ruleset.multipleWinners,
description: 'Description with émojis 🎮🎲',
color: '0xFF000000',
);
@@ -471,7 +474,7 @@ void main() {
test('Game with empty string fields is stored correctly', () async {
final emptyGame = Game(
name: '',
ruleset: '',
ruleset: Ruleset.singleWinner,
description: '',
icon: '',
color: '0xFF000000',
@@ -482,7 +485,7 @@ void main() {
gameId: emptyGame.id,
);
expect(fetchedGame.name, '');
expect(fetchedGame.ruleset, '');
expect(fetchedGame.ruleset, Ruleset.singleWinner);
expect(fetchedGame.description, '');
expect(fetchedGame.icon, '');
});
@@ -493,7 +496,7 @@ void main() {
final longGame = Game(
name: longString,
description: longString,
ruleset: longString,
ruleset: Ruleset.multipleWinners,
color: '0xFF000000',
);
await database.gameDao.addGame(game: longGame);
@@ -503,7 +506,7 @@ void main() {
);
expect(fetchedGame.name.length, 10000);
expect(fetchedGame.description.length, 10000);
expect(fetchedGame.ruleset?.length, 10000);
expect(fetchedGame.ruleset, Ruleset.multipleWinners);
});
// Verifies that multiple sequential updates to the same game work correctly.

View File

@@ -7,6 +7,7 @@ import 'package:game_tracker/data/dto/game.dart';
import 'package:game_tracker/data/dto/group.dart';
import 'package:game_tracker/data/dto/match.dart';
import 'package:game_tracker/data/dto/player.dart';
import 'package:game_tracker/core/enums.dart';
void main() {
late AppDatabase database;
@@ -48,7 +49,7 @@ void main() {
name: 'Test Group 2',
members: [testPlayer4, testPlayer5],
);
testGame = Game(name: 'Test Game', description: 'A test game', color: '0xFF000000');
testGame = Game(name: 'Test Game', ruleset: Ruleset.singleWinner, description: 'A test game', color: '0xFF000000');
testMatch1 = Match(
name: 'First Test Match',
game: testGame,

View File

@@ -8,6 +8,7 @@ import 'package:game_tracker/data/dto/group.dart';
import 'package:game_tracker/data/dto/match.dart';
import 'package:game_tracker/data/dto/player.dart';
import 'package:game_tracker/data/dto/team.dart';
import 'package:game_tracker/core/enums.dart';
void main() {
late AppDatabase database;
@@ -46,7 +47,7 @@ void main() {
name: 'Test Group',
members: [testPlayer1, testPlayer2, testPlayer3],
);
testGame = Game(name: 'Test Game', description: 'A test game', color: '0xFF000000');
testGame = Game(name: 'Test Game', ruleset: Ruleset.singleWinner, description: 'A test game', color: '0xFF000000');
testMatchOnlyGroup = Match(
name: 'Test Match with Group',
game: testGame,

View File

@@ -6,6 +6,7 @@ import 'package:game_tracker/data/db/database.dart';
import 'package:game_tracker/data/dto/game.dart';
import 'package:game_tracker/data/dto/match.dart';
import 'package:game_tracker/data/dto/player.dart';
import 'package:game_tracker/core/enums.dart';
void main() {
late AppDatabase database;
@@ -31,7 +32,7 @@ void main() {
testPlayer1 = Player(name: 'Alice');
testPlayer2 = Player(name: 'Bob');
testPlayer3 = Player(name: 'Charlie');
testGame = Game(name: 'Test Game', description: 'A test game', color: '0xFF000000');
testGame = Game(name: 'Test Game', ruleset: Ruleset.singleWinner, description: 'A test game', color: '0xFF000000');
testMatch1 = Match(
name: 'Test Match 1',
game: testGame,

View File

@@ -7,6 +7,7 @@ import 'package:game_tracker/data/dto/game.dart';
import 'package:game_tracker/data/dto/match.dart';
import 'package:game_tracker/data/dto/player.dart';
import 'package:game_tracker/data/dto/team.dart';
import 'package:game_tracker/core/enums.dart';
void main() {
late AppDatabase database;
@@ -48,8 +49,8 @@ void main() {
name: 'Team Gamma',
members: [testPlayer1, testPlayer3],
);
testGame1 = Game(name: 'Game 1', description: 'Test game 1', color: '0xFF000000');
testGame2 = Game(name: 'Game 2', description: 'Test game 2', color: '0xFF000000');
testGame1 = Game(name: 'Game 1', ruleset: Ruleset.singleWinner, description: 'Test game 1', color: '0xFF000000');
testGame2 = Game(name: 'Game 2', ruleset: Ruleset.highestScore, description: 'Test game 2', color: '0xFF000000');
});
await database.playerDao.addPlayersAsList(