231 lines
7.2 KiB
Dart
231 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();
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|