I'm not sure about that CRUD shit,

but i hope it will work
This commit is contained in:
dan63047 2023-05-20 23:41:01 +03:00
parent 981312b15f
commit e119ecf11b
6 changed files with 210 additions and 88 deletions

View File

@ -62,11 +62,10 @@ class TetrioPlayer {
this.zen, this.zen,
}); });
double get level{ double get level =>
return pow((xp / 500), 0.6) + pow((xp / 500), 0.6) +
(xp / (5000 + (max(0, xp - 4 * pow(10, 6)) / 5000))) + (xp / (5000 + (max(0, xp - 4 * pow(10, 6)) / 5000))) +
1; 1;
}
TetrioPlayer.fromJson(Map<String, dynamic> json, DateTime stateTime) { TetrioPlayer.fromJson(Map<String, dynamic> json, DateTime stateTime) {
userId = json['_id']; userId = json['_id'];
@ -101,11 +100,15 @@ class TetrioPlayer {
var url = Uri.https('ch.tetr.io', 'api/users/$userId/records'); var url = Uri.https('ch.tetr.io', 'api/users/$userId/records');
final response = await http.get(url); final response = await http.get(url);
if (response.statusCode == 200) { if (response.statusCode == 200) {
if(jsonDecode(response.body)['data']['records']['40l']['record'] != null){ if (jsonDecode(response.body)['data']['records']['40l']['record'] !=
sprint.add(RecordSingle.fromJson(jsonDecode(response.body)['data']['records']['40l']['record'])); null) {
sprint.add(RecordSingle.fromJson(
jsonDecode(response.body)['data']['records']['40l']['record']));
} }
if(jsonDecode(response.body)['data']['records']['blitz']['record'] != null){ if (jsonDecode(response.body)['data']['records']['blitz']['record'] !=
blitz.add(RecordSingle.fromJson(jsonDecode(response.body)['data']['records']['blitz']['record'])); null) {
blitz.add(RecordSingle.fromJson(
jsonDecode(response.body)['data']['records']['blitz']['record']));
} }
zen = TetrioZen.fromJson(jsonDecode(response.body)['data']['zen']); zen = TetrioZen.fromJson(jsonDecode(response.body)['data']['zen']);
} else { } else {
@ -120,12 +123,12 @@ class TetrioPlayer {
data['_id'] = userId; data['_id'] = userId;
data['username'] = username; data['username'] = username;
data['role'] = role; data['role'] = role;
data['ts'] = registrationTime; data['ts'] = registrationTime?.toString();
data['badges'] = badges.map((v) => v.toJson()).toList(); data['badges'] = badges.map((v) => v.toJson()).toList();
data['xp'] = xp; data['xp'] = xp;
data['gamesplayed'] = gamesPlayed; data['gamesplayed'] = gamesPlayed;
data['gameswon'] = gamesWon; data['gameswon'] = gamesWon;
data['gametime'] = gameTime; data['gametime'] = gameTime.inMicroseconds / 1000000;
data['country'] = country; data['country'] = country;
data['supporter_tier'] = supporterTier; data['supporter_tier'] = supporterTier;
data['verified'] = verified; data['verified'] = verified;
@ -139,7 +142,7 @@ class TetrioPlayer {
return data; return data;
} }
bool isSameState(TetrioPlayer other){ bool isSameState(TetrioPlayer other) {
if (userId != other.userId) return false; if (userId != other.userId) return false;
if (username != other.username) return false; if (username != other.username) return false;
if (role != other.role) return false; if (role != other.role) return false;
@ -162,7 +165,7 @@ class TetrioPlayer {
} }
@override @override
String toString(){ String toString() {
return "$username ($userId)"; return "$username ($userId)";
} }
@ -190,12 +193,12 @@ class Badge {
final Map<String, dynamic> data = <String, dynamic>{}; final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = badgeId; data['id'] = badgeId;
data['label'] = label; data['label'] = label;
data['ts'] = ts; data['ts'] = ts?.toString();
return data; return data;
} }
@override @override
String toString(){ String toString() {
return "Badge $label ($badgeId)"; return "Badge $label ($badgeId)";
} }
@ -582,6 +585,8 @@ class TetraLeagueAlpha {
percentileRank = json['percentile_rank']; percentileRank = json['percentile_rank'];
} }
double? get app => apm! / (pps! * 60);
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{}; final Map<String, dynamic> data = <String, dynamic>{};
data['gamesplayed'] = gamesPlayed; data['gamesplayed'] = gamesPlayed;

View File

@ -0,0 +1,13 @@
class DatabaseAlreadyOpen implements Exception {}
class DatabaseIsNotOpen implements Exception {}
class UnableToGetDocuments implements Exception {}
class CouldNotDeletePlayer implements Exception {}
class CouldNotUpdatePlayer implements Exception {}
class TetrioPlayerAlreadyExist implements Exception {}
class TetrioPlayerNotExist implements Exception {}

View File

@ -0,0 +1,17 @@
import 'dart:async';
import 'dart:convert';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart'
show MissingPlatformDirectoryException, getApplicationDocumentsDirectory;
import 'package:tetra_stats/services/crud_exceptions.dart';
import 'package:path/path.dart' show join;
const String dbName = "TetraStats.db";
const String tetrioUsersTable = "settings";
const String userTetrioId = "userTetrioId";
const String createSettingsTable = '''
CREATE TABLE IF NOT EXISTS "settings" (
"userTetrioId" TEXT
)''';
class SettingsService {}

View File

@ -0,0 +1,49 @@
import 'dart:async';
import 'dart:convert';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart'
show MissingPlatformDirectoryException, getApplicationDocumentsDirectory;
import 'package:tetra_stats/services/crud_exceptions.dart';
import 'package:tetra_stats/services/tetrio_crud.dart';
import 'package:tetra_stats/services/settings_crud.dart';
import 'package:path/path.dart' show join;
const String dbName = "TetraStats.db";
class DB {
Database? _db;
Future<void> open() async {
if (_db != null) {
throw DatabaseAlreadyOpen();
}
try {
final docsPath = await getApplicationDocumentsDirectory();
final dbPath = join(docsPath.path, dbName);
final db = await openDatabase(dbPath);
_db = db;
await db.execute(createTetrioUsersTable);
await db.execute(createSettingsTable);
} on MissingPlatformDirectoryException {
throw UnableToGetDocuments();
}
}
Future<void> close() async {
final db = _db;
if (db == null) {
throw DatabaseIsNotOpen();
} else {
await db.close();
_db = null;
}
}
Database getDatabaseOrThrow() {
final db = _db;
if (db == null) {
throw DatabaseIsNotOpen();
} else {
return db;
}
}
}

View File

@ -1,9 +1,11 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:sqflite/sqflite.dart'; import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart' show MissingPlatformDirectoryException, getApplicationDocumentsDirectory; import 'package:path_provider/path_provider.dart'
show MissingPlatformDirectoryException, getApplicationDocumentsDirectory;
import 'package:path/path.dart' show join; import 'package:path/path.dart' show join;
import 'package:tetra_stats/services/crud_exceptions.dart';
import 'package:tetra_stats/services/sqlite_db_controller.dart';
import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/data_objects/tetrio.dart';
const String dbName = "TetraStats.db"; const String dbName = "TetraStats.db";
@ -19,89 +21,120 @@ const String createTetrioUsersTable = '''
PRIMARY KEY("id") PRIMARY KEY("id")
);'''; );''';
class DatabaseAlreadyOpen implements Exception {} class TetrioService {
class DatabaseIsNotOpen implements Exception {}
class UnableToGetDocuments implements Exception {}
class CouldNotDeletePlayer implements Exception{}
class TetrioPlayerAlreadyExist implements Exception{}
class TetrioPlayerNotExist implements Exception{}
class TetrioService{
Database? _db;
Map<String, List<TetrioPlayer>> _players = {}; Map<String, List<TetrioPlayer>> _players = {};
final _tetrioStreamController = StreamController<Map<String, List<TetrioPlayer>>>.broadcast(); final _tetrioStreamController =
StreamController<Map<String, List<TetrioPlayer>>>.broadcast();
Database _getDatabaseOrThrow(){ Future<void> _cachePlayers(DB udb) async {
final db = _db; final allPlayers = await getAllPlayers(udb: udb);
if(db == null){ _players = allPlayers.first;
throw DatabaseIsNotOpen(); _tetrioStreamController.add(_players);
}else{
return db;
}
} }
Future<void> open() async{ Future<void> deletePlayer({required String id, required DB udb}) async {
if (_db != null){ final db = udb.getDatabaseOrThrow();
throw DatabaseAlreadyOpen(); final deletedPlayer = await db.delete(tetrioUsersTable,
} where: '$idCol = ?', whereArgs: [id.toLowerCase()]);
try{ if (deletedPlayer != 1) {
final docsPath = await getApplicationDocumentsDirectory();
final dbPath = join(docsPath.path, dbName);
final db = await openDatabase(dbPath);
_db = db;
await db.execute(createTetrioUsersTable);
} on MissingPlatformDirectoryException {
throw UnableToGetDocuments();
}
}
Future<void> close() async{
final db = _db;
if(db == null){
throw DatabaseIsNotOpen();
}else{
await db.close();
_db = null;
}
}
Future<void> _cachePlayers() async{
//final allPlayers = await getAllPlayers();
}
Future<void> deleteTetrioPlayer({required String id}) async{
final db = _getDatabaseOrThrow();
final deletedPlayer = await db.delete(tetrioUsersTable, where: '$idCol = ?', whereArgs: [id.toLowerCase()]);
if (deletedPlayer != 1){
throw CouldNotDeletePlayer(); throw CouldNotDeletePlayer();
} else {
_players.removeWhere((key, value) => key == id);
_tetrioStreamController.add(_players);
} }
} }
Future<void> storeUser({required TetrioPlayer tetrioPlayer}) async{ // Future <List<TetrioPlayer>> getOrCreatePlayer({required String id}) async {
final db = _getDatabaseOrThrow(); // try{
final results = await db.query(tetrioUsersTable, limit: 1, where: '$idCol = ?', whereArgs: [tetrioPlayer.userId.toLowerCase()]); // final player = await getPlayer(id: id);
if(results.isNotEmpty){ // return player;
// } on TetrioPlayerNotExist{
// final player = await createPlayer(tetrioPlayer: tetrioPlayer)
// }
// }
Future<void> createPlayer(
{required TetrioPlayer tetrioPlayer, required DB udb}) async {
final db = udb.getDatabaseOrThrow();
final results = await db.query(tetrioUsersTable,
limit: 1,
where: '$idCol = ?',
whereArgs: [tetrioPlayer.userId.toLowerCase()]);
if (results.isNotEmpty) {
throw TetrioPlayerAlreadyExist(); throw TetrioPlayerAlreadyExist();
} }
final Map<String, String> statesJson = {tetrioPlayer.state.toString(): tetrioPlayer.toJson().toString()}; final Map<String, dynamic> statesJson = {
tetrioPlayer.state.millisecondsSinceEpoch.toString():
tetrioPlayer.toJson()
};
db.insert(tetrioUsersTable, { db.insert(tetrioUsersTable, {
idCol: tetrioPlayer.userId, idCol: tetrioPlayer.userId,
nickCol: tetrioPlayer.username, nickCol: tetrioPlayer.username,
statesCol: statesJson statesCol: jsonEncode(statesJson)
}); });
_players.addEntries({
tetrioPlayer.userId: [tetrioPlayer]
}.entries);
_tetrioStreamController.add(_players);
} }
Future<List<TetrioPlayer>> getUser({required String id}) async{ Future<void> storeState(TetrioPlayer tetrioPlayer, DB udb) async {
final db = _getDatabaseOrThrow(); final db = udb.getDatabaseOrThrow();
List<TetrioPlayer> states =
await getPlayer(id: tetrioPlayer.userId, udb: udb);
states.add(tetrioPlayer);
final Map<String, dynamic> statesJson = {};
for (var e in states) {
statesJson.addEntries(
{e.state.millisecondsSinceEpoch.toString(): e.toJson()}.entries);
}
db.update(
tetrioUsersTable,
{
idCol: tetrioPlayer.userId,
nickCol: tetrioPlayer.username,
statesCol: jsonEncode(statesJson)
},
where: '$idCol = ?',
whereArgs: [tetrioPlayer.userId]);
_players[tetrioPlayer.userId]!.add(tetrioPlayer);
_tetrioStreamController.add(_players);
}
Future<List<TetrioPlayer>> getPlayer(
{required String id, required DB udb}) async {
final db = udb.getDatabaseOrThrow();
List<TetrioPlayer> states = []; List<TetrioPlayer> states = [];
final results = await db.query(tetrioUsersTable, limit: 1, where: '$idCol = ?', whereArgs: [id.toLowerCase()]); final results = await db.query(tetrioUsersTable,
if(results.isEmpty){ limit: 1, where: '$idCol = ?', whereArgs: [id.toLowerCase()]);
if (results.isEmpty) {
throw TetrioPlayerNotExist(); throw TetrioPlayerNotExist();
}else{ } else {
dynamic rawStates = results.first['jsonStates'] as String; dynamic rawStates = results.first['jsonStates'] as String;
rawStates = json.decode(rawStates); rawStates = json.decode(rawStates);
rawStates.forEach((k,v) => states.add(TetrioPlayer.fromJson(v, DateTime.now()))); rawStates.forEach((k, v) => states.add(TetrioPlayer.fromJson(
v, DateTime.fromMillisecondsSinceEpoch(int.parse(k)))));
_players.removeWhere((key, value) => key == id);
_players.addEntries({states.last.userId: states}.entries);
_tetrioStreamController.add(_players);
return states; return states;
} }
} }
}
Future<Iterable<Map<String, List<TetrioPlayer>>>> getAllPlayers(
{required DB udb}) async {
//await _ensureDbIsOpen();
final db = udb.getDatabaseOrThrow();
final players = await db.query(tetrioUsersTable);
Map<String, List<TetrioPlayer>> data = {};
return players.map((row) {
var test = json.decode(row['jsonStates'] as String);
List<TetrioPlayer> states = [];
test.forEach(
(k, v) => states.add(TetrioPlayer.fromJson(v, DateTime.now())));
data.addEntries({states.last.userId: states}.entries);
return data;
});
}
}

View File

@ -3,9 +3,11 @@ import 'package:http/http.dart' as http;
import 'dart:convert'; import 'dart:convert';
import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:tetra_stats/services/tetrio_crud.dart'; import 'package:tetra_stats/services/tetrio_crud.dart';
import 'package:tetra_stats/services/sqlite_db_controller.dart';
String _searchFor = ""; String _searchFor = "";
late TetrioPlayer me; late TetrioPlayer me;
DB db = DB();
TetrioService teto = TetrioService(); TetrioService teto = TetrioService();
class MainView extends StatefulWidget { class MainView extends StatefulWidget {
@ -18,14 +20,18 @@ class MainView extends StatefulWidget {
class _MainViewState extends State<MainView> { class _MainViewState extends State<MainView> {
Future<TetrioPlayer> fetchTetrioPlayer(String user) async { Future<TetrioPlayer> fetchTetrioPlayer(String user) async {
var url = Uri.https('ch.tetr.io', 'api/users/$user'); var url = Uri.https('ch.tetr.io', 'api/users/$user');
teto.open(); db.open();
final response = await http.get(url); final response = await http.get(url);
// final response = await http.get(Uri.parse('https://ch.tetr.io/')); // final response = await http.get(Uri.parse('https://ch.tetr.io/'));
if (response.statusCode == 200) { if (response.statusCode == 200) {
// If the server did return a 200 OK response, // If the server did return a 200 OK response,
// then parse the JSON. // then parse the JSON.
return TetrioPlayer.fromJson(jsonDecode(response.body)['data']['user'], DateTime.fromMillisecondsSinceEpoch(jsonDecode(response.body)['cache']['cached_at'], isUtc: true)); return TetrioPlayer.fromJson(
jsonDecode(response.body)['data']['user'],
DateTime.fromMillisecondsSinceEpoch(
jsonDecode(response.body)['cache']['cached_at'],
isUtc: true));
} else { } else {
// If the server did not return a 200 OK response, // If the server did not return a 200 OK response,
// then throw an exception. // then throw an exception.
@ -54,9 +60,6 @@ class _MainViewState extends State<MainView> {
child: TextField( child: TextField(
onChanged: (String value) { onChanged: (String value) {
_searchFor = value; _searchFor = value;
setState(() {
});
}, },
onSubmitted: (String value) { onSubmitted: (String value) {
setState(() { setState(() {
@ -77,7 +80,7 @@ class _MainViewState extends State<MainView> {
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.hasData) { if (snapshot.hasData) {
snapshot.data!.getRecords(); snapshot.data!.getRecords();
teto.getUser(id: snapshot.data!.userId); teto.storeState(snapshot.data!, db);
return Flexible( return Flexible(
child: Column(children: [ child: Column(children: [
Text(snapshot.data!.username.toString()), Text(snapshot.data!.username.toString()),
@ -105,9 +108,11 @@ class _MainViewState extends State<MainView> {
Text( Text(
"${snapshot.data!.tlSeason1.standing} (№${snapshot.data!.tlSeason1.standingLocal} in country)"), "${snapshot.data!.tlSeason1.standing} (№${snapshot.data!.tlSeason1.standingLocal} in country)"),
Text( Text(
"${snapshot.data!.tlSeason1.apm} APM, ${snapshot.data!.tlSeason1.pps} PPS, ${snapshot.data!.tlSeason1.vs} VS"), "${snapshot.data!.tlSeason1.apm} APM, ${snapshot.data!.tlSeason1.pps} PPS, ${snapshot.data!.tlSeason1.vs} VS, ${snapshot.data!.tlSeason1.app?.toStringAsFixed(3)} APP"),
const Text("\n40 Lines", softWrap: true), const Text("\n40 Lines", softWrap: true),
Text(snapshot.data!.sprint.isNotEmpty ? snapshot.data!.sprint[0].toString() : "No record"), Text(snapshot.data!.sprint.isNotEmpty
? snapshot.data!.sprint[0].toString()
: "No record"),
])); ]));
} else if (snapshot.hasError) { } else if (snapshot.hasError) {
return Text('${snapshot.error}'); return Text('${snapshot.error}');