Compare commits
3 Commits
80672343b9
...
c43b7b478c
| Author | SHA1 | Date | |
|---|---|---|---|
| c43b7b478c | |||
| c9188c222a | |||
| fcca74cea5 |
@@ -24,16 +24,17 @@ class Group {
|
|||||||
return 'Group{id: $id, name: $name, description: $description, members: $members}';
|
return 'Group{id: $id, name: $name, description: $description, members: $members}';
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a Group instance from a JSON object (memberIds format).
|
/// Creates a Group instance from a JSON object where the related [Player]
|
||||||
/// Player objects are reconstructed from memberIds by the DataTransferService.
|
/// objects are represented by their IDs.
|
||||||
Group.fromJson(Map<String, dynamic> json)
|
Group.fromJson(Map<String, dynamic> json)
|
||||||
: id = json['id'],
|
: id = json['id'],
|
||||||
createdAt = DateTime.parse(json['createdAt']),
|
createdAt = DateTime.parse(json['createdAt']),
|
||||||
name = json['name'],
|
name = json['name'],
|
||||||
description = json['description'],
|
description = json['description'],
|
||||||
members = []; // Populated during import via DataTransferService
|
members = [];
|
||||||
|
|
||||||
/// Converts the Group instance to a JSON object using normalized format (memberIds only).
|
/// Converts the Group instance to a JSON object. Related [Player] objects are
|
||||||
|
/// represented by their IDs.
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
'id': id,
|
'id': id,
|
||||||
'createdAt': createdAt.toIso8601String(),
|
'createdAt': createdAt.toIso8601String(),
|
||||||
|
|||||||
@@ -38,8 +38,9 @@ class Match {
|
|||||||
return 'Match{id: $id, createdAt: $createdAt, endedAt: $endedAt, name: $name, game: $game, group: $group, players: $players, notes: $notes, scores: $scores, winner: $winner}';
|
return 'Match{id: $id, createdAt: $createdAt, endedAt: $endedAt, name: $name, game: $game, group: $group, players: $players, notes: $notes, scores: $scores, winner: $winner}';
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a Match instance from a JSON object (ID references format).
|
/// Creates a Match instance from a JSON object where related objects are
|
||||||
/// Related objects are reconstructed from IDs by the DataTransferService.
|
/// represented by their IDs. Therefore, the game, group, and players are not
|
||||||
|
/// fully constructed here.
|
||||||
Match.fromJson(Map<String, dynamic> json)
|
Match.fromJson(Map<String, dynamic> json)
|
||||||
: id = json['id'],
|
: id = json['id'],
|
||||||
createdAt = DateTime.parse(json['createdAt']),
|
createdAt = DateTime.parse(json['createdAt']),
|
||||||
@@ -53,13 +54,15 @@ class Match {
|
|||||||
description: '',
|
description: '',
|
||||||
color: GameColor.blue,
|
color: GameColor.blue,
|
||||||
icon: '',
|
icon: '',
|
||||||
), // Populated during import via DataTransferService
|
),
|
||||||
group = null, // Populated during import via DataTransferService
|
group = null,
|
||||||
players = [], // Populated during import via DataTransferService
|
players = [],
|
||||||
scores = json['scores'],
|
scores = json['scores'],
|
||||||
notes = json['notes'] ?? '';
|
notes = json['notes'] ?? '';
|
||||||
|
|
||||||
/// Converts the Match instance to a JSON object using normalized format (ID references only).
|
/// Converts the Match instance to a JSON object. Related objects are
|
||||||
|
/// represented by their IDs, so the game, group, and players are not fully
|
||||||
|
/// serialized here.
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
'id': id,
|
'id': id,
|
||||||
'createdAt': createdAt.toIso8601String(),
|
'createdAt': createdAt.toIso8601String(),
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ class Team {
|
|||||||
createdAt = DateTime.parse(json['createdAt']),
|
createdAt = DateTime.parse(json['createdAt']),
|
||||||
members = []; // Populated during import via DataTransferService
|
members = []; // Populated during import via DataTransferService
|
||||||
|
|
||||||
/// Converts the Team instance to a JSON object using normalized format (memberIds only).
|
/// Converts the Team instance to a JSON object. Related objects are
|
||||||
|
/// represented by their IDs.
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
'id': id,
|
'id': id,
|
||||||
'name': name,
|
'name': name,
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ class DataTransferService {
|
|||||||
/// Deletes all data from the database.
|
/// Deletes all data from the database.
|
||||||
static Future<void> deleteAllData(BuildContext context) async {
|
static Future<void> deleteAllData(BuildContext context) async {
|
||||||
final db = Provider.of<AppDatabase>(context, listen: false);
|
final db = Provider.of<AppDatabase>(context, listen: false);
|
||||||
|
|
||||||
await db.matchDao.deleteAllMatches();
|
await db.matchDao.deleteAllMatches();
|
||||||
await db.teamDao.deleteAllTeams();
|
await db.teamDao.deleteAllTeams();
|
||||||
await db.groupDao.deleteAllGroups();
|
await db.groupDao.deleteAllGroups();
|
||||||
@@ -96,8 +97,8 @@ class DataTransferService {
|
|||||||
/// Exports the given JSON string to a file with the specified name.
|
/// Exports the given JSON string to a file with the specified name.
|
||||||
/// Returns an [ExportResult] indicating the outcome.
|
/// Returns an [ExportResult] indicating the outcome.
|
||||||
///
|
///
|
||||||
/// [jsonString] The JSON string to be exported.
|
/// - [jsonString]: The JSON string to be exported.
|
||||||
/// [fileName] The desired name for the exported file (without extension).
|
/// - [fileName]: The desired name for the exported file (without extension).
|
||||||
static Future<ExportResult> exportData(
|
static Future<ExportResult> exportData(
|
||||||
String jsonString,
|
String jsonString,
|
||||||
String fileName,
|
String fileName,
|
||||||
@@ -163,21 +164,22 @@ class DataTransferService {
|
|||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
static Future<void> importDataToDatabase(
|
static Future<void> importDataToDatabase(
|
||||||
AppDatabase db,
|
AppDatabase db,
|
||||||
Map<String, dynamic> decoded,
|
Map<String, dynamic> decodedJson,
|
||||||
) async {
|
) async {
|
||||||
final importedPlayers = parsePlayersFromJson(decoded);
|
// Fetch all entities first to create lookup maps for relationships
|
||||||
|
final importedPlayers = parsePlayersFromJson(decodedJson);
|
||||||
final playerById = {for (final p in importedPlayers) p.id: p};
|
final playerById = {for (final p in importedPlayers) p.id: p};
|
||||||
|
|
||||||
final importedGames = parseGamesFromJson(decoded);
|
final importedGames = parseGamesFromJson(decodedJson);
|
||||||
final gameById = {for (final g in importedGames) g.id: g};
|
final gameById = {for (final g in importedGames) g.id: g};
|
||||||
|
|
||||||
final importedGroups = parseGroupsFromJson(decoded, playerById);
|
final importedGroups = parseGroupsFromJson(decodedJson, playerById);
|
||||||
final groupById = {for (final g in importedGroups) g.id: g};
|
final groupById = {for (final g in importedGroups) g.id: g};
|
||||||
|
|
||||||
final importedTeams = parseTeamsFromJson(decoded, playerById);
|
final importedTeams = parseTeamsFromJson(decodedJson, playerById);
|
||||||
|
|
||||||
final importedMatches = parseMatchesFromJson(
|
final importedMatches = parseMatchesFromJson(
|
||||||
decoded,
|
decodedJson,
|
||||||
gameById,
|
gameById,
|
||||||
groupById,
|
groupById,
|
||||||
playerById,
|
playerById,
|
||||||
@@ -190,31 +192,30 @@ class DataTransferService {
|
|||||||
await db.matchDao.addMatchAsList(matches: importedMatches);
|
await db.matchDao.addMatchAsList(matches: importedMatches);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses players from JSON data.
|
/* Parsing Methods */
|
||||||
|
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
static List<Player> parsePlayersFromJson(Map<String, dynamic> decoded) {
|
static List<Player> parsePlayersFromJson(Map<String, dynamic> decodedJson) {
|
||||||
final playersJson = (decoded['players'] as List<dynamic>?) ?? [];
|
final playersJson = (decodedJson['players'] as List<dynamic>?) ?? [];
|
||||||
return playersJson
|
return playersJson
|
||||||
.map((p) => Player.fromJson(p as Map<String, dynamic>))
|
.map((p) => Player.fromJson(p as Map<String, dynamic>))
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses games from JSON data.
|
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
static List<Game> parseGamesFromJson(Map<String, dynamic> decoded) {
|
static List<Game> parseGamesFromJson(Map<String, dynamic> decodedJson) {
|
||||||
final gamesJson = (decoded['games'] as List<dynamic>?) ?? [];
|
final gamesJson = (decodedJson['games'] as List<dynamic>?) ?? [];
|
||||||
return gamesJson
|
return gamesJson
|
||||||
.map((g) => Game.fromJson(g as Map<String, dynamic>))
|
.map((g) => Game.fromJson(g as Map<String, dynamic>))
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses groups from JSON data.
|
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
static List<Group> parseGroupsFromJson(
|
static List<Group> parseGroupsFromJson(
|
||||||
Map<String, dynamic> decoded,
|
Map<String, dynamic> decodedJson,
|
||||||
Map<String, Player> playerById,
|
Map<String, Player> playerById,
|
||||||
) {
|
) {
|
||||||
final groupsJson = (decoded['groups'] as List<dynamic>?) ?? [];
|
final groupsJson = (decodedJson['groups'] as List<dynamic>?) ?? [];
|
||||||
return groupsJson.map((g) {
|
return groupsJson.map((g) {
|
||||||
final map = g as Map<String, dynamic>;
|
final map = g as Map<String, dynamic>;
|
||||||
final memberIds = (map['memberIds'] as List<dynamic>? ?? [])
|
final memberIds = (map['memberIds'] as List<dynamic>? ?? [])
|
||||||
@@ -238,10 +239,10 @@ class DataTransferService {
|
|||||||
/// Parses teams from JSON data.
|
/// Parses teams from JSON data.
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
static List<Team> parseTeamsFromJson(
|
static List<Team> parseTeamsFromJson(
|
||||||
Map<String, dynamic> decoded,
|
Map<String, dynamic> decodedJson,
|
||||||
Map<String, Player> playerById,
|
Map<String, Player> playerById,
|
||||||
) {
|
) {
|
||||||
final teamsJson = (decoded['teams'] as List<dynamic>?) ?? [];
|
final teamsJson = (decodedJson['teams'] as List<dynamic>?) ?? [];
|
||||||
return teamsJson.map((t) {
|
return teamsJson.map((t) {
|
||||||
final map = t as Map<String, dynamic>;
|
final map = t as Map<String, dynamic>;
|
||||||
final memberIds = (map['memberIds'] as List<dynamic>? ?? [])
|
final memberIds = (map['memberIds'] as List<dynamic>? ?? [])
|
||||||
@@ -264,46 +265,53 @@ class DataTransferService {
|
|||||||
/// Parses matches from JSON data.
|
/// Parses matches from JSON data.
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
static List<Match> parseMatchesFromJson(
|
static List<Match> parseMatchesFromJson(
|
||||||
Map<String, dynamic> decoded,
|
Map<String, dynamic> decodedJson,
|
||||||
Map<String, Game> gameById,
|
Map<String, Game> gamesMap,
|
||||||
Map<String, Group> groupById,
|
Map<String, Group> groupsMap,
|
||||||
Map<String, Player> playerById,
|
Map<String, Player> playersMap,
|
||||||
) {
|
) {
|
||||||
final matchesJson = (decoded['matches'] as List<dynamic>?) ?? [];
|
final matchesJson = (decodedJson['matches'] as List<dynamic>?) ?? [];
|
||||||
return matchesJson.map((m) {
|
return matchesJson.map((m) {
|
||||||
final map = m as Map<String, dynamic>;
|
final map = m as Map<String, dynamic>;
|
||||||
|
|
||||||
|
// Extract attributes from json
|
||||||
|
final id = map['id'] as String;
|
||||||
|
final name = map['name'] as String;
|
||||||
final gameId = map['gameId'] as String;
|
final gameId = map['gameId'] as String;
|
||||||
final groupId = map['groupId'] as String?;
|
final groupId = map['groupId'] as String?;
|
||||||
final playerIds = (map['playerIds'] as List<dynamic>? ?? [])
|
final createdAt = DateTime.parse(map['createdAt'] as String);
|
||||||
.cast<String>();
|
|
||||||
final endedAt = map['endedAt'] != null
|
final endedAt = map['endedAt'] != null
|
||||||
? DateTime.parse(map['endedAt'] as String)
|
? DateTime.parse(map['endedAt'] as String)
|
||||||
: null;
|
: null;
|
||||||
|
final notes = map['notes'] as String? ?? '';
|
||||||
|
|
||||||
final game = gameById[gameId] ?? createUnknownGame();
|
// Link attributes to objects
|
||||||
final group = groupId != null ? groupById[groupId] : null;
|
final game = gamesMap[gameId] ?? getFallbackGame();
|
||||||
|
final group = groupId != null ? groupsMap[groupId] : null;
|
||||||
|
|
||||||
|
final playerIds = (map['playerIds'] as List<dynamic>? ?? [])
|
||||||
|
.cast<String>();
|
||||||
final players = playerIds
|
final players = playerIds
|
||||||
.map((id) => playerById[id])
|
.map((id) => playersMap[id])
|
||||||
.whereType<Player>()
|
.whereType<Player>()
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
return Match(
|
return Match(
|
||||||
id: map['id'] as String,
|
id: id,
|
||||||
name: map['name'] as String,
|
name: name,
|
||||||
game: game,
|
game: game,
|
||||||
group: group,
|
group: group,
|
||||||
players: players,
|
players: players,
|
||||||
createdAt: DateTime.parse(map['createdAt'] as String),
|
createdAt: createdAt,
|
||||||
endedAt: endedAt,
|
endedAt: endedAt,
|
||||||
notes: map['notes'] as String? ?? '',
|
notes: notes,
|
||||||
);
|
);
|
||||||
}).toList();
|
}).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a fallback game when the referenced game is not found.
|
/// Creates a fallback game when the referenced game is not found.
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
static Game createUnknownGame() {
|
static Game getFallbackGame() {
|
||||||
return Game(
|
return Game(
|
||||||
name: 'Unknown',
|
name: 'Unknown',
|
||||||
ruleset: Ruleset.singleWinner,
|
ruleset: Ruleset.singleWinner,
|
||||||
@@ -320,7 +328,8 @@ class DataTransferService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Validates the given JSON string against the predefined schema.
|
/// Validates the given JSON string against the schema
|
||||||
|
/// in `assets/schema.json`.
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
static Future<bool> validateJsonSchema(String jsonString) async {
|
static Future<bool> validateJsonSchema(String jsonString) async {
|
||||||
final String schemaString;
|
final String schemaString;
|
||||||
|
|||||||
@@ -588,6 +588,18 @@ void main() {
|
|||||||
expect(games[0].ruleset, testGame.ruleset);
|
expect(games[0].ruleset, testGame.ruleset);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('parseGamesFromJson() empty list', () {
|
||||||
|
final jsonMap = {'games': []};
|
||||||
|
final games = DataTransferService.parseGamesFromJson(jsonMap);
|
||||||
|
expect(games, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('parseGamesFromJson() missing key', () {
|
||||||
|
final jsonMap = <String, dynamic>{};
|
||||||
|
final games = DataTransferService.parseGamesFromJson(jsonMap);
|
||||||
|
expect(games, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
test('parseGroupsFromJson()', () {
|
test('parseGroupsFromJson()', () {
|
||||||
final playerById = {
|
final playerById = {
|
||||||
testPlayer1.id: testPlayer1,
|
testPlayer1.id: testPlayer1,
|
||||||
@@ -619,6 +631,18 @@ void main() {
|
|||||||
expect(groups[0].members[1].id, testPlayer2.id);
|
expect(groups[0].members[1].id, testPlayer2.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('parseGroupsFromJson() empty list', () {
|
||||||
|
final jsonMap = {'groups': []};
|
||||||
|
final groups = DataTransferService.parseGroupsFromJson(jsonMap, {});
|
||||||
|
expect(groups, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('parseGroupsFromJson() missing key', () {
|
||||||
|
final jsonMap = <String, dynamic>{};
|
||||||
|
final groups = DataTransferService.parseGroupsFromJson(jsonMap, {});
|
||||||
|
expect(groups, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
test('parseGroupsFromJson() ignores invalid player ids', () {
|
test('parseGroupsFromJson() ignores invalid player ids', () {
|
||||||
final playerById = {testPlayer1.id: testPlayer1};
|
final playerById = {testPlayer1.id: testPlayer1};
|
||||||
|
|
||||||
@@ -670,6 +694,18 @@ void main() {
|
|||||||
expect(teams[0].members[0].id, testPlayer1.id);
|
expect(teams[0].members[0].id, testPlayer1.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('parseTeamsFromJson() empty list', () {
|
||||||
|
final jsonMap = {'teams': []};
|
||||||
|
final teams = DataTransferService.parseTeamsFromJson(jsonMap, {});
|
||||||
|
expect(teams, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('parseTeamsFromJson() missing key', () {
|
||||||
|
final jsonMap = <String, dynamic>{};
|
||||||
|
final teams = DataTransferService.parseTeamsFromJson(jsonMap, {});
|
||||||
|
expect(teams, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
test('parseMatchesFromJson()', () {
|
test('parseMatchesFromJson()', () {
|
||||||
final playerById = {
|
final playerById = {
|
||||||
testPlayer1.id: testPlayer1,
|
testPlayer1.id: testPlayer1,
|
||||||
@@ -707,6 +743,28 @@ void main() {
|
|||||||
expect(matches[0].players.length, 2);
|
expect(matches[0].players.length, 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('parseMatchesFromJson() empty list', () {
|
||||||
|
final jsonMap = {'teams': []};
|
||||||
|
final matches = DataTransferService.parseMatchesFromJson(
|
||||||
|
jsonMap,
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
expect(matches, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('parseMatchesFromJson() missing key', () {
|
||||||
|
final jsonMap = <String, dynamic>{};
|
||||||
|
final matches = DataTransferService.parseMatchesFromJson(
|
||||||
|
jsonMap,
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
expect(matches, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
test('parseMatchesFromJson() creates unknown game for missing game', () {
|
test('parseMatchesFromJson() creates unknown game for missing game', () {
|
||||||
final playerById = {testPlayer1.id: testPlayer1};
|
final playerById = {testPlayer1.id: testPlayer1};
|
||||||
final gameById = <String, Game>{};
|
final gameById = <String, Game>{};
|
||||||
|
|||||||
Reference in New Issue
Block a user