diff --git a/Sources/bowlin_project/lib/database/fields/FriendFields.dart b/Sources/bowlin_project/lib/database/fields/FriendFields.dart new file mode 100644 index 0000000..327d16a --- /dev/null +++ b/Sources/bowlin_project/lib/database/fields/FriendFields.dart @@ -0,0 +1,10 @@ +class GameFields { + static final List values = [ + id, date, pointsCurrentUser, userId + ]; + + static final String id = '_id'; + static final String date = '_date'; + static final String pointsCurrentUser = '_points_current_user'; + static final String userId = '_user_id'; +} \ No newline at end of file diff --git a/Sources/bowlin_project/lib/database/fields/StatFields.dart b/Sources/bowlin_project/lib/database/fields/StatFields.dart index 554ba52..e6bed82 100644 --- a/Sources/bowlin_project/lib/database/fields/StatFields.dart +++ b/Sources/bowlin_project/lib/database/fields/StatFields.dart @@ -1,4 +1,16 @@ class StatFields { + static final List values = [ + idUser, nbVictory, nbGames, highscore, nbStrikes, nbSpares, nbScore, avgScore, avgPinsPerRound + ]; + static final String nbVictory = '_nbVictory'; + static final String nbGames = '_nbGames'; + static final String highscore = '_highscore'; + static final String nbStrikes = '_nbStrikes'; + static final String nbSpares = '_nbSpares'; + static final String nbScore = '_nbScore'; + static final String avgScore = '_avgScore'; + static final String avgPinsPerRound = '_avgPinsPerRound'; + static final String idUser = '_idUser'; } \ No newline at end of file diff --git a/Sources/bowlin_project/lib/database/mappers/StatMapper.dart b/Sources/bowlin_project/lib/database/mappers/StatMapper.dart new file mode 100644 index 0000000..d668541 --- /dev/null +++ b/Sources/bowlin_project/lib/database/mappers/StatMapper.dart @@ -0,0 +1,32 @@ +import 'package:bowl_in/database/fields/StatFields.dart'; +import '../../model/Stat.dart'; +import '../../model/User.dart'; + +class StatMapper { + static Map toJson(Stat stat, User user) { + return { + StatFields.idUser: user.id, + StatFields.nbVictory: stat.nbVictory, + StatFields.nbGames: stat.nbGames, + StatFields.highscore: stat.highscore, + StatFields.nbStrikes: stat.nbStrikes, + StatFields.nbSpares: stat.nbSpares, + StatFields.nbScore: stat.nbScore, + StatFields.avgScore: stat.avgScore, + StatFields.avgPinsPerRound: stat.avgPinsPerRound + }; + } + + static Stat toModel(Map json) { + return Stat( + json[StatFields.nbVictory], + json[StatFields.nbGames], + json[StatFields.highscore], + json[StatFields.nbStrikes], + json[StatFields.nbSpares], + json[StatFields.nbScore], + json[StatFields.avgScore], + json[StatFields.avgPinsPerRound] + ); + } +} diff --git a/Sources/bowlin_project/lib/database/mappers/UserMapper.dart b/Sources/bowlin_project/lib/database/mappers/UserMapper.dart index 225753e..7aa14a8 100644 --- a/Sources/bowlin_project/lib/database/mappers/UserMapper.dart +++ b/Sources/bowlin_project/lib/database/mappers/UserMapper.dart @@ -1,3 +1,4 @@ +import '../../model/Stat.dart'; import '../../model/User.dart'; import '../fields/UserFields.dart'; @@ -11,14 +12,15 @@ class UserMapper { }; } - static User toModel(Map json) { - return User( + static User toModel(Map json, Stat stat) { + return User.withStat( json[UserFields.id], json[UserFields.name], json[UserFields.image], json[UserFields.mail], [], [], + stat ); } -} \ No newline at end of file +} diff --git a/Sources/bowlin_project/lib/database/sqlflite/BowlInDatabase.dart b/Sources/bowlin_project/lib/database/sqlflite/BowlInDatabase.dart new file mode 100644 index 0000000..b79d556 --- /dev/null +++ b/Sources/bowlin_project/lib/database/sqlflite/BowlInDatabase.dart @@ -0,0 +1,251 @@ +import 'package:bowl_in/database/mappers/GameMapper.dart'; +import 'package:bowl_in/database/mappers/StatMapper.dart'; +import 'package:bowl_in/model/Game.dart'; +import 'package:bowl_in/model/User.dart'; +import 'package:path/path.dart'; +import 'package:sqflite/sqflite.dart'; + +import '../../model/Stat.dart'; +import '../fields/GameDetailFields.dart'; +import '../fields/GameFields.dart'; +import '../fields/StatFields.dart'; +import '../fields/UserFields.dart'; +import '../mappers/UserMapper.dart'; + +class BowlInDatabase { + BowlInDatabase(); + + static final BowlInDatabase instance = BowlInDatabase._init(); + + static Database? _database; + + BowlInDatabase._init(); + + static const String tableUser = 'users'; + static const String tableGame = 'games'; + static const String tableGameDetail = 'gameDetails'; + static const String tableStat = 'stats'; + + Future get database async { + if (_database != null) return _database!; + + _database = await _initDB('user.db'); + return _database!; + } + + Future _initDB(String filePath) async { + final dbPath = await getDatabasesPath(); + final path = join(dbPath, filePath); + + return await openDatabase(path, + version: 1, onCreate: _createDB, onUpgrade: _upgradeDB); + } + + Future _createDB(Database db, int version) async { + const idType = 'INTEGER PRIMARY KEY AUTOINCREMENT'; + const textType = 'TEXT NOT NULL'; + const boolType = 'BOOLEAN NOT NULL'; + const integerType = 'INTEGER NOT NULL'; + const realType = 'REAL NOT NULL'; + + await db.execute(''' +CREATE TABLE $tableUser ( + ${UserFields.id} $idType, + ${UserFields.name} $boolType, + ${UserFields.image} $textType, + ${UserFields.mail} $textType + ) +'''); + + await db.execute(''' +CREATE TABLE $tableGame ( + ${GameFields.id} $idType, + ${GameFields.date} $textType, + ${GameFields.pointsCurrentUser} $integerType, + ${GameFields.userId} $integerType, + FOREIGN KEY(${GameFields.userId}) REFERENCES $tableUser(${UserFields.id}) +) +'''); + + await db.execute(''' +CREATE TABLE $tableGameDetail ( + ${GameDetailFields.id} $idType, + ${GameDetailFields.date} $textType, + ${GameDetailFields.nameWinner} $textType, + ${GameDetailFields.host} $textType +) +'''); + + await db.execute(''' +CREATE TABLE $tableStat ( + ${StatFields.idUser} $integerType, + ${StatFields.nbVictory} $integerType, + ${StatFields.nbGames} $integerType, + ${StatFields.highscore} $integerType, + ${StatFields.nbStrikes} $integerType, + ${StatFields.nbSpares} $integerType, + ${StatFields.nbScore} $integerType, + ${StatFields.avgScore} $realType, + ${StatFields.avgPinsPerRound} $realType, + FOREIGN KEY(${StatFields.idUser}) REFERENCES $tableUser(${UserFields.id}) +) +'''); + } + + Future _upgradeDB(Database db, int oldVersion, int newVersion) async { + if (oldVersion == 5) { + await db.execute('DROP TABLE IF EXISTS $tableUser'); + await db.execute('DROP TABLE IF EXISTS $tableGame'); + await db.execute('DROP TABLE IF EXISTS $tableGameDetail'); + await db.execute('DROP TABLE IF EXISTS $tableStat'); + await _createDB(db, newVersion); + } + } + + // User + + Future createUser(User user) async { + final db = await instance.database; + await db.insert(tableUser, UserMapper.toJson(user)); + await createStat(user); + } + + Future readUser(int id) async { + final db = await instance.database; + final result = await db + .query(tableUser, where: '${UserFields.id} = ?', whereArgs: [id]); + + if (result.isNotEmpty) { + final stat = await readStat(id); + + User user; + if (stat != null) { + user = UserMapper.toModel(result.first, stat); + } + else { + user = UserMapper.toModel(result.first, Stat.empty()); + } + //final games = await readGame(id); + //for (var game in games!) { + //user.games.add(game); + //} + return user; + } else { + return null; + } + } + + Future updateUser(User user) async { + final db = await instance.database; + + await db.transaction((txn) async { + await txn.update(tableUser, UserMapper.toJson(user), + where: '${UserFields.id} = ?', whereArgs: [user.id]); + + await txn.update(tableStat, StatMapper.toJson(user.stat, user), + where: '${StatFields.idUser} = ?', whereArgs: [user.id]); + + // Insert new games for the user + //await createGame(user); + }); + } + + Future deleteUser(int id) async { + final db = await instance.database; + + await deleteGame(id); + + await deleteStat(id); + + return await db.delete( + tableUser, + where: '${UserFields.id} = ?', + whereArgs: [id], + ); + } + + // GameDetail + + // Game + + Future createGame(User user) async { + final db = await instance.database; + + await db.transaction((txn) async { + for (var game in user.games) { + await txn.insert(tableGame, GameMapper.toJson(game, user)); + } + }); + } + + Future?> readGame(int id) async { + final db = await instance.database; + final result = await db.query(tableGame, + where: '${GameFields.userId} = ?', whereArgs: [id]); + + if (result.isNotEmpty) { + List games = []; + for (var game in result) { + games.add(GameMapper.toModel(game)); + } + return games; + } else { + return null; + } + } + + Future deleteGame(int id) async { + final db = await instance.database; + + return await db.delete( + tableGame, + where: '${GameFields.userId} = ?', + whereArgs: [id], + ); + } + + // Stat + + Future createStat(User user) async { + final db = await instance.database; + await db.insert(tableStat, StatMapper.toJson(Stat.empty(), user)); + } + + Future readStat(int id) async { + final db = await instance.database; + final result = await db.query(tableStat, + where: '${StatFields.idUser} = ?', whereArgs: [id]); + + if (result.isNotEmpty) { + Stat stat = StatMapper.toModel(result.first); + return stat; + } else { + return null; + } + } + + Future updateStat(User user) async { + final db = await instance.database; + await db.transaction((txn) async { + await txn.update(tableStat, StatMapper.toJson(user.stat, user), + where: '${StatFields.idUser} = ?', whereArgs: [user.id]); + }); + } + + Future deleteStat(int id) async { + final db = await instance.database; + + return await db.delete( + tableStat, + where: '${StatFields.idUser} = ?', + whereArgs: [id], + ); + + } + + Future close() async { + final db = await instance.database; + + db.close(); + } +} diff --git a/Sources/bowlin_project/lib/database/sqlflite/UserDataBase.dart b/Sources/bowlin_project/lib/database/sqlflite/UserDataBase.dart deleted file mode 100644 index 5104443..0000000 --- a/Sources/bowlin_project/lib/database/sqlflite/UserDataBase.dart +++ /dev/null @@ -1,135 +0,0 @@ -import 'package:bowl_in/database/mappers/GameMapper.dart'; -import 'package:bowl_in/model/User.dart'; -import 'package:path/path.dart'; -import 'package:sqflite/sqflite.dart'; - -import '../fields/GameDetailFields.dart'; -import '../fields/GameFields.dart'; -import '../fields/UserFields.dart'; -import '../mappers/UserMapper.dart'; - -class BowlInDatabase { - BowlInDatabase(); - - static final BowlInDatabase instance = BowlInDatabase._init(); - - static Database? _database; - - BowlInDatabase._init(); - - static const String tableUser = 'users'; - static const String tableGame = 'games'; - static const String tableGameDetail = 'gameDetails'; - - Future get database async { - if (_database != null) return _database!; - - _database = await _initDB('user.db'); - return _database!; - } - - Future _initDB(String filePath) async { - final dbPath = await getDatabasesPath(); - final path = join(dbPath, filePath); - - return await openDatabase(path, version: 4, onCreate: _createDB, onUpgrade: _upgradeDB); - } - - Future _createDB(Database db, int version) async { - const idType = 'INTEGER PRIMARY KEY AUTOINCREMENT'; - const textType = 'TEXT NOT NULL'; - const boolType = 'BOOLEAN NOT NULL'; - final integerType = 'INTEGER NOT NULL'; - - await db.execute(''' -CREATE TABLE $tableUser ( - ${UserFields.id} $idType, - ${UserFields.name} $boolType, - ${UserFields.image} $textType, - ${UserFields.mail} $textType - ) -'''); - - await db.execute(''' -CREATE TABLE $tableGame ( - ${GameFields.id} $idType, - ${GameFields.date} $textType, - ${GameFields.pointsCurrentUser} $integerType, - ${GameFields.userId} $integerType, - FOREIGN KEY(${GameFields.userId}) REFERENCES $tableUser(${UserFields.id}) -) -'''); - - await db.execute(''' -CREATE TABLE $tableGameDetail ( - ${GameDetailFields.id} $idType, - ${GameDetailFields.date} $textType, - ${GameDetailFields.nameWinner} $textType, - ${GameDetailFields.host} $textType, -) -'''); - } - - Future _upgradeDB(Database db, int oldVersion, int newVersion) async { - if (oldVersion < 4) { - await db.execute('DROP TABLE IF EXISTS $tableUser'); - await db.execute('DROP TABLE IF EXISTS $tableGame'); - await db.execute('DROP TABLE IF EXISTS $tableGameDetail'); - await _createDB(db, newVersion); - } - } - - Future createUser(User user) async { - final db = await instance.database; - await db.insert(tableUser, UserMapper.toJson(user)); - } - - Future readUser(int id) async { - final db = await instance.database; - final result = await db - .query(tableUser, where: '${UserFields.id} = ?', whereArgs: [id]); - - if (result.isNotEmpty) { - User user = UserMapper.toModel(result.first); - final games = await db - .query(tableGame, where: '${GameFields.userId} = ?', whereArgs: [user.id]); - for (var game in games) { - user.games.add(GameMapper.toModel(game)); - } - return user; - } - else { - return null; - } - } - - Future updateUser(User user) async { - final db = await instance.database; - - await db.transaction((txn) async { - await txn.update(tableUser, UserMapper.toJson(user), - where: '${UserFields.id} = ?', whereArgs: [user.id]); - - // Insert new games for the user - for (var game in user.games) { - await txn.insert(tableGame, GameMapper.toJson(game, user)); - } - }); - } - - Future deleteUser(int id) async { - final db = await instance.database; - - return await db.delete( - tableUser, - where: '${UserFields.id} = ?', - whereArgs: [id], - ); - } - - Future close() async { - final db = await instance.database; - - db.close(); - } -} diff --git a/Sources/bowlin_project/lib/main.dart b/Sources/bowlin_project/lib/main.dart index 56cd39a..d6faae1 100644 --- a/Sources/bowlin_project/lib/main.dart +++ b/Sources/bowlin_project/lib/main.dart @@ -1,5 +1,4 @@ import 'package:bowl_in/model/LocalManager/LocalData.dart'; -import 'package:bowl_in/model/StubManager/StubData.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:bowl_in/config/app_router.dart'; @@ -11,7 +10,7 @@ void main() { } class MyApp extends StatelessWidget { - static IManager controller = StubData(); + static IManager controller = LocalData(); const MyApp({super.key}); diff --git a/Sources/bowlin_project/lib/model/GamePlayer.dart b/Sources/bowlin_project/lib/model/GamePlayer.dart index 61af1ad..7eb41df 100644 --- a/Sources/bowlin_project/lib/model/GamePlayer.dart +++ b/Sources/bowlin_project/lib/model/GamePlayer.dart @@ -40,6 +40,7 @@ class GamePlayer { _parent.gameMgr.addGame(game); game.computeScores(); + _parent.userMgr.saveUser(_parent.userCurrent); context?.go("/scoreboard", extra: game); } else { print("IN GAME : " + currentRoundIndex.toString()); diff --git a/Sources/bowlin_project/lib/model/IUserManager.dart b/Sources/bowlin_project/lib/model/IUserManager.dart index b9d5ed6..e5a7e89 100644 --- a/Sources/bowlin_project/lib/model/IUserManager.dart +++ b/Sources/bowlin_project/lib/model/IUserManager.dart @@ -12,5 +12,6 @@ abstract class IUserManager { IAuthManager get authMgr => _authMgr; List getUsersByName(String name); Player getUserById(int id); + saveUser(User user); List getRankingWithFriends(); } diff --git a/Sources/bowlin_project/lib/model/LocalManager/GameManager.dart b/Sources/bowlin_project/lib/model/LocalManager/GameManager.dart index b93f3a0..dc111f7 100644 --- a/Sources/bowlin_project/lib/model/LocalManager/GameManager.dart +++ b/Sources/bowlin_project/lib/model/LocalManager/GameManager.dart @@ -3,57 +3,89 @@ import 'package:bowl_in/model/IGameManager.dart'; import 'package:bowl_in/model/LocalManager/LocalData.dart'; import 'package:bowl_in/model/Player.dart'; +import '../User.dart'; + class GameManager extends IGameManager { final LocalData parent; - + // Constructor GameManager(this.parent); - @override - addGame(GameDetail gd) { - // TODO: implement addGame - throw UnimplementedError(); + // Methods + GameDetail getGameById(int id) { + for (var element in parent.gameDetails) { + if (element.id == id) { + return element; + } + } + throw Exception("Game not found."); } - @override - GameDetail getGameById(int id) { - // TODO: implement getGameById - throw UnimplementedError(); + List getGamesByPlayerId(int id) { + List games = []; + for (var element in parent.gameDetails) { + for (Player player in element.players) { + if (player is User && player.id == id) { + games.add(element); + break; + } + } + } + return games; } - @override List getGamesByPlayer(Player user) { - // TODO: implement getGamesByPlayer - throw UnimplementedError(); + List games = []; + for (var element in parent.gameDetails) { + for (Player player in element.players) { + if (player is User && user is User && player.id == user.id) { + games.add(element); + break; + } + } + } + return games; } - @override - List getGamesByPlayerId(int id) { - // TODO: implement getGamesByPlayerId - throw UnimplementedError(); + List getGamesByPlayers(List users) { + List games = []; + for (var element in parent.gameDetails) { + if (element.players.toSet().containsAll(users.toSet())) { + games.add(element); + } + } + return games; } - @override - List getGamesByPlayers(List users) { - // TODO: implement getGamesByPlayers - throw UnimplementedError(); + List getPlayersByIdGame(int id) { + List players = []; + for (var element in parent.gameDetails) { + if (element.id == id) { + for (var player in element.players) { + players.add(player); + } + return players; + } + } + throw Exception("Game not found."); } - @override - int getNextId() { - // TODO: implement getNextId - throw UnimplementedError(); + Map getRankByIdGame(int id) { + for (var game in parent.gameDetails) { + if (game.id == id) { + return game.getRank(); + } + } + throw Exception("Game not found."); } @override - List getPlayersByIdGame(int id) { - // TODO: implement getPlayersByIdGame - throw UnimplementedError(); + addGame(GameDetail gd) { + parent.gameDetails.add(gd); } @override - Map getRankByIdGame(int id) { - // TODO: implement getRankByIdGame - throw UnimplementedError(); + int getNextId() { + return parent.gameDetails.length; } -} \ No newline at end of file +} diff --git a/Sources/bowlin_project/lib/model/LocalManager/LocalData.dart b/Sources/bowlin_project/lib/model/LocalManager/LocalData.dart index eb840b5..3dfb52d 100644 --- a/Sources/bowlin_project/lib/model/LocalManager/LocalData.dart +++ b/Sources/bowlin_project/lib/model/LocalManager/LocalData.dart @@ -1,13 +1,19 @@ -import 'package:bowl_in/database/sqlflite/UserDataBase.dart'; +import 'package:bowl_in/database/sqlflite/BowlInDatabase.dart'; import 'package:bowl_in/model/IManager.dart'; +import '../GameDetail.dart'; import 'GameManager.dart'; import 'UserManager.dart'; class LocalData extends IManager { - final BowlInDatabase userDatabase = BowlInDatabase(); + final BowlInDatabase database = BowlInDatabase(); LocalData() { userMgr = UserManager(this); gameMgr = GameManager(this); } + + List _gameDetails = []; + + List get gameDetails => _gameDetails; + } diff --git a/Sources/bowlin_project/lib/model/LocalManager/UserManager.dart b/Sources/bowlin_project/lib/model/LocalManager/UserManager.dart index c88f5ec..250fc30 100644 --- a/Sources/bowlin_project/lib/model/LocalManager/UserManager.dart +++ b/Sources/bowlin_project/lib/model/LocalManager/UserManager.dart @@ -2,7 +2,6 @@ import 'package:bowl_in/model/IUserManager.dart'; import 'package:bowl_in/model/LocalManager/LocalData.dart'; import 'package:bowl_in/model/Player.dart'; import 'package:bowl_in/model/User.dart'; -import '../Game.dart'; import 'AuthManager.dart'; class UserManager extends IUserManager { @@ -10,14 +9,15 @@ class UserManager extends IUserManager { // Constructor UserManager(this.parent) : super(AuthManager(parent)) { - User user = User(0, "Dave", "./assets/images/image_user_red.png", "", [], []); - user.games.add(Game(0,DateTime.now(), 30, [])); - saveUser(user); + //User user = User(0, "Victor", "./assets/images/image_user_red.png", "", [], []); + //saveUser(user); + //parent.database.deleteUser(0); + //test(); _initUser(); } _initUser() async { - var user = await parent.userDatabase.readUser(0); + var user = await parent.database.readUser(0); if (user == null) { User user2 = User(1, "Unknown", "./assets/images/image_user_cyan.png", "", [], []); @@ -27,8 +27,13 @@ class UserManager extends IUserManager { } } - saveUser(User user) { - parent.userDatabase.updateUser(user); + saveUser(User user) async { + var result = await parent.database.readUser(0); + if (result == null) { + await parent.database.createUser(user); + } else { + await parent.database.updateUser(user); + } } Map userToMap(User user) { @@ -42,8 +47,9 @@ class UserManager extends IUserManager { @override List getRankingWithFriends() { - // TODO: implement getRankingWithFriends - throw UnimplementedError(); + List sortedPlayers = List.from(parent.userCurrent.friends); + sortedPlayers.sort((a, b) => b.stat.highscore.compareTo(a.stat.highscore)); + return sortedPlayers; } @override diff --git a/Sources/bowlin_project/lib/model/StubManager/StubData.dart b/Sources/bowlin_project/lib/model/StubManager/StubData.dart index d5d36f8..8499b82 100644 --- a/Sources/bowlin_project/lib/model/StubManager/StubData.dart +++ b/Sources/bowlin_project/lib/model/StubManager/StubData.dart @@ -11,6 +11,7 @@ import 'UserManager.dart'; import 'GameManager.dart'; class StubData extends IManager { + StubData() { userMgr = UserManager(this); gameMgr = GameManager(this); diff --git a/Sources/bowlin_project/lib/model/StubManager/UserManager.dart b/Sources/bowlin_project/lib/model/StubManager/UserManager.dart index fd8d9e9..e016c60 100644 --- a/Sources/bowlin_project/lib/model/StubManager/UserManager.dart +++ b/Sources/bowlin_project/lib/model/StubManager/UserManager.dart @@ -38,4 +38,9 @@ class UserManager extends IUserManager { sortedPlayers.sort((a, b) => b.stat.highscore.compareTo(a.stat.highscore)); return sortedPlayers; } + + @override + saveUser(User user) { + return ; + } } diff --git a/Sources/bowlin_project/lib/model/User.dart b/Sources/bowlin_project/lib/model/User.dart index 7bcd9f9..582785c 100644 --- a/Sources/bowlin_project/lib/model/User.dart +++ b/Sources/bowlin_project/lib/model/User.dart @@ -8,12 +8,18 @@ class User extends Player { String _mail; List _achievements = []; List _friends = []; - final Stat _stat = Stat.empty(); + late final Stat _stat; List games = []; // Constructor User(this._id, String name, String image, this._mail, this._achievements, this._friends) + : super(name, image){ + _stat = Stat.empty(); + } + + User.withStat(this._id, String name, String image, this._mail, this._achievements, + this._friends, this._stat) : super(name, image); int get id => _id;