ohthatsa/lib/pages/practice/PracticeDatabase.dart
2025-06-21 17:08:45 +02:00

168 lines
No EOL
4.4 KiB
Dart

import 'dart:async';
import 'dart:core';
import 'package:ohthatsa/pages/practice/PracticeAnswer.dart';
import 'package:ohthatsa/pages/practice/PracticeType.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class PracticeDatabase {
static _onCreate(Database database, int version) async {
String types = PracticeType.values.map((type) => "'${type.toString().split(".").last}'").join(", ");
await database.execute('''
CREATE TABLE PracticeSession (
id INTEGER PRIMARY KEY AUTOINCREMENT,
type TEXT CHECK( type IN ($types) ) NOT NULL
);
''');
await database.execute('''
CREATE TABLE PracticeAnswer (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sessionId INTEGER NOT NULL,
question INTEGER NOT NULL,
answer INTEGER NOT NULL,
time DATETIME NOT NULL,
correct BOOLEAN NOT NULL,
FOREIGN KEY(sessionId) REFERENCES PracticeSession(id)
);
''');
await database.execute('''
CREATE VIEW AnswersView AS
SELECT
PracticeSession.id as practice_id,
PracticeAnswer.id as answer_id,
PracticeSession.type as type,
PracticeAnswer.question as question,
PracticeAnswer.answer as answer,
PracticeAnswer.correct as correct,
PracticeAnswer.time as time
FROM PracticeSession
INNER JOIN PracticeAnswer on PracticeSession.id = PracticeAnswer.sessionId;
''');
await database.execute('''
CREATE VIEW AnswersCorrectByTypeViewAll AS
SELECT
type,
SUM(correct = 1)*1.0 AS correct,
SUM(correct = 0)*1.0 AS incorrect,
COUNT(*) AS total
FROM
AnswersView
GROUP BY
type;
''');
await database.execute('''
CREATE VIEW AnswersCorrectByTypeView7d AS
SELECT
type,
SUM(correct = 1)*1.0 AS correct,
SUM(correct = 0)*1.0 AS incorrect,
COUNT(*) AS total,
CAST(
strftime('%s', time)
AS INTEGER
) as s_time,
CAST(
strftime("%s", DATETIME('now', '-7 day'))
AS INTEGER
) as before
FROM
AnswersView
WHERE
s_time > before
GROUP BY
type;
''');
await database.execute('''
CREATE VIEW AnswersCorrectByTypeView30d AS
SELECT
type,
SUM(correct = 1)*1.0 AS correct,
SUM(correct = 0)*1.0 AS incorrect,
COUNT(*) AS total,
CAST(
strftime('%s', time)
AS INTEGER
) as s_time,
CAST(
strftime("%s", DATETIME('now', '-1 month'))
AS INTEGER
) as before
FROM
AnswersView
WHERE
s_time > before
GROUP BY
type;
''');
}
static Future<Database> getDatabase() async {
var databasesPath = await getDatabasesPath();
final databaseName = "answers.db";
return await openDatabase(
join(databasesPath, databaseName),
version: 1,
onCreate: _onCreate
);
}
static insertAnswers(List<PracticeAnswer> answers, PracticeType practiceType) async {
var db = await getDatabase();
int sessionId = await db.insert("PracticeSession",
<String, dynamic>{
"type": practiceType.toString().split(".").last
}
);
var batch = db.batch();
answers.forEach((answer) {
answer.sessionId = sessionId;
batch.insert("PracticeAnswer", answer.toMap());
});
await batch.commit(noResult: true);
}
// TODO test the database
static Future<Map<String, double>> getStats() async {
var db = await getDatabase();
List<Map<String, dynamic>> answers = await db.rawQuery('''
SELECT
"All" AS range,
type,
correct,
incorrect,
total,
ROUND((correct / total) * 100, 1) AS ratio
FROM
AnswersCorrectByTypeViewAll
UNION ALL
SELECT
"7d" AS range,
type,
correct,
incorrect,
total,
ROUND((correct / total) * 100, 1) AS ratio
FROM
AnswersCorrectByTypeView7d
UNION ALL
SELECT
"30d" AS range,
type,
correct,
incorrect,
total,
ROUND((correct / total) * 100, 1) AS ratio
FROM
AnswersCorrectByTypeView30d;
''');
Map<String, double> stats = Map<String, double>();
for(var stat in answers){
var typeRange = "${stat['range']} ${stat['type']}";
stats[typeRange] = stat['ratio'] as double;
}
return stats;
}
}