admin
Some checks are pending
Backend CI / test (push) Waiting to run
Backend CI / build (push) Blocked by required conditions
Web App CI / test (push) Waiting to run
Web App CI / build (push) Blocked by required conditions
Deploy Mnemo Cards / Deploy Backend (push) Waiting to run
Deploy Mnemo Cards / Deploy Web App (push) Blocked by required conditions
Deploy Mnemo Cards / Final Verification (push) Blocked by required conditions

This commit is contained in:
Dmitry 2025-12-20 19:27:45 +03:00
parent 4751594b9a
commit 5e3061165f
5 changed files with 62 additions and 9 deletions

View file

@ -12,6 +12,12 @@ class InputButtonsQuestionGenerator implements QuestionGenerator {
final Random random;
final Set<InputButtonsQuestionType>? possibleTypes;
// UUID regex pattern (same as in test_manager.dart)
static final _uuidRegex = RegExp(
r'^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$',
caseSensitive: false,
);
InputButtonsQuestionGenerator(
this.data, {
this.seed,
@ -36,6 +42,17 @@ class InputButtonsQuestionGenerator implements QuestionGenerator {
return types[random.nextInt(types.length)];
}
/// Checks if a string is a valid UUID
bool _isUuid(String value) => _uuidRegex.hasMatch(value.trim());
/// Gets audio UUID from answerItem.audio if it's a valid UUID, otherwise returns null
/// This allows using UUID from MinIO instead of text-based audio identifiers
String? _getAudioUuid(TestDataItem answerItem) {
final audioValue = answerItem.audio;
if (audioValue == null || audioValue.trim().isEmpty) return null;
return _isUuid(audioValue) ? audioValue.trim() : null;
}
Future<InputButtonsTestQuestionBody> inputButtonsTestQuestion(
TestDataItem answerItem, {
InputButtonsQuestionType? type,
@ -58,7 +75,8 @@ class InputButtonsQuestionGenerator implements QuestionGenerator {
String? questionAudio = null;
if (questionType.audioQuestion) {
questionAudio = 'es-ES_${answerItem.audio}';
// Use UUID from MinIO if available, otherwise null
questionAudio = _getAudioUuid(answerItem);
} else if (withAudio) {
// nothing to fo here
}

View file

@ -11,6 +11,12 @@ class SimpleQuestionGenerator implements QuestionGenerator {
final Random random;
final Set<SimpleQuestionType>? possibleTypes;
// UUID regex pattern (same as in test_manager.dart)
static final _uuidRegex = RegExp(
r'^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$',
caseSensitive: false,
);
SimpleQuestionGenerator(
this.data, {
this.seed,
@ -36,6 +42,17 @@ class SimpleQuestionGenerator implements QuestionGenerator {
return types[random.nextInt(types.length)];
}
/// Checks if a string is a valid UUID
bool _isUuid(String value) => _uuidRegex.hasMatch(value.trim());
/// Gets audio UUID from answerCard.audio if it's a valid UUID, otherwise returns null
/// This allows using UUID from MinIO instead of text-based audio identifiers
String? _getAudioUuid(TestDataItem answerCard) {
final audioValue = answerCard.audio;
if (audioValue == null || audioValue.trim().isEmpty) return null;
return _isUuid(audioValue) ? audioValue.trim() : null;
}
Future<SimpleTestQuestionBody> simpleTestQuestion(
TestDataItem answerCard, {
SimpleQuestionType? type,
@ -57,14 +74,18 @@ class SimpleQuestionGenerator implements QuestionGenerator {
String? questionAudio = null;
if (questionType.audioQuestion) {
if (questionType.translationAnswers || questionType.imagesAnswers) {
questionAudio = 'es-ES_${answerCard.audio}';
// Use UUID from MinIO if available, otherwise null
questionAudio = _getAudioUuid(answerCard);
}
} else if (withAudio) {
if (questionText != null && questionType.originalQuestion) {
questionAudio = 'es-ES_$questionText';
// For original questions with text, audio is handled separately
// Use UUID if available, otherwise null
questionAudio = _getAudioUuid(answerCard);
} else if (questionType.translationAnswers ||
questionType.imagesAnswers) {
questionAudio = 'es-ES_${answerCard.audio}';
// Use UUID from MinIO if available, otherwise null
questionAudio = _getAudioUuid(answerCard);
}
}
final answers = [answerCard, ...otherCards]..shuffle();

View file

@ -677,15 +677,29 @@ class TestManager {
}
// Create creation test data
final testDataItems = cards.map((card) {
// Get voices for all cards to use UUID from MinIO instead of text
final testDataItems = await Future.wait(cards.map((card) async {
// Get voices for this card
final voices = await _db.packDao.getCardVoices(card.id.toString());
// Use first voice's voiceUrl if it's a valid UUID, otherwise null
String? audioUuid;
if (voices.isNotEmpty) {
final voiceUrl = voices.first.voiceUrl.trim();
// Check if voiceUrl is a valid UUID (object ID in MinIO)
if (_isUuid(voiceUrl)) {
audioUuid = voiceUrl;
}
}
return TestDataItem(
id: card.id.toString(),
original: card.original,
translation: card.translation,
image: card.image,
audio: card.original, // Use original as audio
audio: audioUuid, // Use UUID from MinIO if available
);
}).toList();
}));
final creationTestData = CreationTestData(
items: testDataItems,

View file

@ -434,7 +434,7 @@ class TestsStateManager extends StateManager<TestsState> {
questions.add(GameQuestion.multipleChoice(
MultipleChoiceQuestion(
id: 'q_${questions.length}',
question: question.text ?? '',
question: question.w ?? '',
image: question.imageUrl ?? question.image, // Use presigned URL if available
audio: question.audio,
options: options, // Keep for backward compatibility

View file

@ -44,7 +44,7 @@ class AnswerOptions extends StatelessWidget {
// Adjust aspect ratio for image buttons (they need more space)
final hasImages = hasOptionItems &&
question.optionItems.any((item) => item.image != null);
final childAspectRatio = hasImages ? 2.5 : 4.0;
final childAspectRatio = hasImages ? 1.1 : 4.0;
return GridView.builder(
shrinkWrap: true,