230 lines
7.2 KiB
Dart
230 lines
7.2 KiB
Dart
import 'dart:convert';
|
||
import 'dart:io';
|
||
|
||
import 'package:injectable/injectable.dart';
|
||
import 'package:isar/isar.dart';
|
||
import 'package:mnemo_cards_common_backend/mnemo_cards_common_backend.dart';
|
||
import 'package:mnemo_cards_backend/packs/pack_manager.dart';
|
||
import 'package:mnemo_cards_backend/tests/generators/models/creation_test_data.dart';
|
||
import 'package:mnemo_cards_backend/tests/test_extension.dart';
|
||
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
|
||
|
||
import '../main.dart';
|
||
import '../packs/pack_dto_converter.dart';
|
||
import 'generators/question_generators/input_buttons_question_generator.dart';
|
||
import 'generators/pack_test_generator.dart';
|
||
import 'generators/question_generators/simple_question_generator.dart';
|
||
|
||
@lazySingleton
|
||
class TestManager {
|
||
final PackDtoConverter _packDtoConverter;
|
||
List<CreationTestData> _customCreationTestData = [];
|
||
|
||
TestManager(this._packDtoConverter);
|
||
|
||
Future<TestStatisticsDto?> _testStatisticsDto(
|
||
UserModel user, int testId) async {
|
||
return (await user.userData.value?.testsStatistics
|
||
.filter()
|
||
.test((q) => q.idEqualTo(testId))
|
||
.findFirst())
|
||
?.toDto();
|
||
}
|
||
|
||
Future<TestDto?> fetchTest(String id, UserModel user) async {
|
||
return (await isar.testModels.get(int.parse(id)))?.toDto(
|
||
statistics: await _testStatisticsDto(user, int.parse(id)),
|
||
);
|
||
}
|
||
|
||
Future<List<TestDto>> availableTests(UserModel userModel) async {
|
||
final tests = (await isar.testModels.where().findAll());
|
||
final dtos = await Future.wait(tests.map(
|
||
(test) async => test.toDto(
|
||
questions: [],
|
||
statistics: await _testStatisticsDto(userModel, test.id!),
|
||
),
|
||
));
|
||
return dtos.toList();
|
||
}
|
||
|
||
Future<List<TestDto>> fetchPackTests(
|
||
UserModel user,
|
||
CardPackModel model,
|
||
) async {
|
||
final pack = (await isar.cardPackModels.get(model.id!))!;
|
||
final Iterable<TestModel> testModels;
|
||
if (await _updateGeneratedTestsIfRequired(pack)) {
|
||
testModels = (await isar.cardPackModels.get(model.id!))!.tests;
|
||
} else {
|
||
testModels = pack.tests;
|
||
}
|
||
return (await Future.wait(
|
||
testModels.map(
|
||
(e) async => e.toDto(
|
||
statistics: await _testStatisticsDto(user, e.id!),
|
||
),
|
||
),
|
||
))
|
||
.toList();
|
||
}
|
||
|
||
Future<bool> _updateGeneratedTestsIfRequired(CardPackModel model) async {
|
||
final generated = model.tests.where((t) => t.version == 'generated');
|
||
if (generated.isEmpty) {
|
||
await updateGeneratedTests(model);
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
Future<void> refreshCustomCreationTestsData() async {
|
||
final dir = Directory('${PackManager.assetsDirectory.path}/tests/');
|
||
List<CreationTestData> data = [];
|
||
if (await dir.exists()) {
|
||
final files = dir.listSync().whereType<File>().toList();
|
||
await Future.wait(
|
||
files.map(
|
||
(file) => file
|
||
.readAsString()
|
||
.then((s) => data.add(
|
||
s.decode(CreationTestData.fromJson),
|
||
))
|
||
.catchError(
|
||
(err) {
|
||
print('Error when loading custom test data: ${file.path}');
|
||
print(err);
|
||
},
|
||
),
|
||
),
|
||
);
|
||
}
|
||
_customCreationTestData = data;
|
||
}
|
||
|
||
Future<void> updateGeneratedTests(CardPackModel model) async {
|
||
final oldGenerated =
|
||
model.tests.where((t) => t.version == 'generated').toList();
|
||
final cardPackDto = _packDtoConverter.toDto(model);
|
||
final packCreationTestData = CreationTestData.fromCardPackDto(cardPackDto);
|
||
final customCreationTestData = _customCreationTestData.where(
|
||
(data) => data.packId == cardPackDto.id,
|
||
);
|
||
final generator = PackTestGenerator(packCreationTestData);
|
||
final testDtos = [
|
||
await generator.generate(
|
||
name: 'Мини тест',
|
||
multiply: 1.0,
|
||
ratios: {
|
||
TestQuestionType.simple: 1.0,
|
||
TestQuestionType.input_buttons: 0.1,
|
||
},
|
||
),
|
||
for (final customData in customCreationTestData)
|
||
await PackTestGenerator(
|
||
customData,
|
||
color: customData.color ?? cardPackDto.color,
|
||
generators: {
|
||
TestQuestionType.simple: SimpleQuestionGenerator(
|
||
customData,
|
||
possibleTypes: {
|
||
SimpleQuestionType.original_translation,
|
||
SimpleQuestionType.translation_original,
|
||
SimpleQuestionType.audio_translation,
|
||
},
|
||
),
|
||
},
|
||
).generate(
|
||
name: customData.title,
|
||
multiply: 1.0,
|
||
ratios: {
|
||
TestQuestionType.simple: 1.0,
|
||
},
|
||
),
|
||
await PackTestGenerator(
|
||
packCreationTestData,
|
||
generators: {
|
||
TestQuestionType.simple: SimpleQuestionGenerator.images(
|
||
packCreationTestData,
|
||
),
|
||
TestQuestionType.input_buttons: InputButtonsQuestionGenerator.images(
|
||
packCreationTestData,
|
||
),
|
||
},
|
||
).generate(
|
||
name: 'Тест с картинками',
|
||
multiply: 2.0,
|
||
ratios: {
|
||
TestQuestionType.simple: 1.0,
|
||
TestQuestionType.input_buttons: 0.25,
|
||
},
|
||
),
|
||
await generator.generate(
|
||
name: 'Тест на написание',
|
||
multiply: 2.0,
|
||
ratios: {
|
||
TestQuestionType.input_buttons: 1.0,
|
||
},
|
||
),
|
||
await generator.generate(
|
||
name: 'Большой тест',
|
||
multiply: 1.5,
|
||
ratios: {
|
||
TestQuestionType.simple: 1.0,
|
||
TestQuestionType.input_buttons: 1.0,
|
||
},
|
||
),
|
||
];
|
||
|
||
isar.writeTxn(() async {
|
||
for (final test in testDtos) {
|
||
final testModel = test.toEmptyModel();
|
||
final questionModels = test.questions
|
||
.map(
|
||
(e) => TestQuestionModel(
|
||
questionType: e.questionType,
|
||
body: jsonEncode(e.toJson()),
|
||
),
|
||
)
|
||
.toList();
|
||
await isar.testQuestionModels.putAll(questionModels);
|
||
testModel.questions.addAll(questionModels);
|
||
testModel.packs.add(model);
|
||
await isar.testModels.put(testModel);
|
||
await testModel.packs.save();
|
||
await testModel.questions.save();
|
||
}
|
||
if (oldGenerated.isNotEmpty) {
|
||
for (final test in oldGenerated) {
|
||
await test.questions.load();
|
||
final questions =
|
||
test.questions.map((e) => e.id).whereNotNull().toList();
|
||
if (questions.isNotEmpty) {
|
||
await isar.testQuestionModels.deleteAll(questions);
|
||
}
|
||
await isar.testModels.delete(test.id!);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
Future<void> addTest(TestDto testDto) async {
|
||
await isar.writeTxn(() async {
|
||
final questions = <TestQuestionModel>[];
|
||
for (final q in testDto.questions.where((q) => q.body != null)) {
|
||
final model = TestQuestionModel(
|
||
id: q.id,
|
||
questionType: q.questionType,
|
||
body: q.body!,
|
||
);
|
||
await isar.testQuestionModels.put(model);
|
||
questions.add(model);
|
||
}
|
||
final testModel = testDto.toEmptyModel();
|
||
await isar.testModels.put(testModel);
|
||
await testModel.questions
|
||
..addAll(questions)
|
||
..save();
|
||
});
|
||
}
|
||
}
|