-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #22 from ia-toki/18-task-quiz-make-presentation-of…
…-quiz-exercise quiz exercise
- Loading branch information
Showing
28 changed files
with
850 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
183 changes: 183 additions & 0 deletions
183
app/lib/features/quiz_exercise/presentation/bloc/quiz_exercise_cubit.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
import 'dart:async'; | ||
|
||
import 'package:equatable/equatable.dart'; | ||
import 'package:flutter_bloc/flutter_bloc.dart'; | ||
|
||
import '../../../../models/weekly_quiz.dart'; | ||
import '../../../../services/quiz_service.dart'; | ||
import '../model/quiz_exercise.dart'; | ||
import '../model/quiz_exercise_answer.dart'; | ||
import '../model/quiz_exercise_attempt.dart'; | ||
import '../repositories/quiz_exercise.dart'; | ||
|
||
part 'quiz_exercise_event.dart'; | ||
part 'quiz_exercise_state.dart'; | ||
|
||
class QuizExerciseCubit extends Cubit<QuizExerciseState> { | ||
QuizExerciseCubit() : super(QuizExerciseInitialState()); | ||
|
||
late WeeklyQuiz quiz; | ||
late QuizExercise currentProblem; | ||
int currentProblemIndex = 0; | ||
late List<String> problemIdList; | ||
late QuizExerciseAttempt attempt; | ||
late String quizParticipantId; | ||
late String challengeGroup; | ||
|
||
String selectedAnswer = ''; | ||
late int remainingDuration; | ||
|
||
late QuizService quizService; | ||
late QuizExerciseRepository quizExerciseRepository; | ||
|
||
FutureOr<void> fetchQuizExercise( | ||
{String? quizId, | ||
String? quizParticipantId, | ||
String? challengeGroup}) async { | ||
try { | ||
this.quizService = QuizService(); | ||
this.quizExerciseRepository = QuizExerciseRepository(); | ||
|
||
emit(QuizExerciseLoading()); | ||
if (quizId == null) { | ||
throw Exception('Quiz Id null'); | ||
} | ||
if (quizParticipantId == null) { | ||
throw Exception('Quiz Participant Id null'); | ||
} | ||
if (challengeGroup == null) { | ||
throw Exception('Challenge Group null'); | ||
} | ||
this.quizParticipantId = quizParticipantId; | ||
this.challengeGroup = challengeGroup; | ||
|
||
quiz = await quizService.getQuiz(quizId); | ||
|
||
var _problemIdList = quiz.problems[challengeGroup]; | ||
if (_problemIdList == null) { | ||
throw Exception('Challenge Group not found'); | ||
} | ||
problemIdList = _problemIdList; | ||
if (problemIdList == null) { | ||
throw Exception('Task for selected Challenge Group not found'); | ||
} | ||
if (problemIdList.isEmpty) { | ||
throw Exception('Problem Set empty'); | ||
} | ||
|
||
var weeklyQuizParticipant = await quizService.getWeeklyQuizParticipant( | ||
quizParticipantId: quizParticipantId); | ||
if (weeklyQuizParticipant.attempts.isEmpty) { | ||
attempt = QuizExerciseAttempt( | ||
startAt: DateTime.now(), | ||
totalBlank: problemIdList.length, | ||
totalCorrect: 0, | ||
totalIncorrect: 0, | ||
answers: []); | ||
} | ||
|
||
currentProblem = | ||
await quizExerciseRepository.getQuizExercise(problemIdList.first); | ||
|
||
final duration = quiz.duration_minute[challengeGroup]; | ||
if (duration == null) { | ||
throw Exception('Duration for selected Challenge Group not found'); | ||
} | ||
remainingDuration = duration * 60; | ||
Timer.periodic(const Duration(seconds: 1), (timer) { | ||
if (state is! QuizExerciseShow) { | ||
timer.cancel(); | ||
} else if (remainingDuration > 0) { | ||
remainingDuration--; | ||
emit(QuizExerciseShow( | ||
quiz: quiz, | ||
quizExercise: currentProblem, | ||
remainingDuration: Duration(seconds: remainingDuration), | ||
selectedAnswer: selectedAnswer, | ||
)); | ||
} else { | ||
timer.cancel(); | ||
emit(QuizExerciseFailed('Duration Ends')); | ||
} | ||
}); | ||
emit(QuizExerciseShow( | ||
quiz: quiz, | ||
quizExercise: currentProblem, | ||
remainingDuration: Duration(seconds: remainingDuration), | ||
selectedAnswer: selectedAnswer, | ||
)); | ||
} catch (e) { | ||
emit(QuizExerciseFailed(e.toString())); | ||
} | ||
} | ||
|
||
void selectAnswer(String answerId) { | ||
selectedAnswer = answerId; | ||
emit(QuizExerciseShow( | ||
quiz: quiz, | ||
quizExercise: currentProblem, | ||
remainingDuration: Duration(seconds: remainingDuration), | ||
selectedAnswer: selectedAnswer, | ||
)); | ||
} | ||
|
||
FutureOr<void> submitAnswer() async { | ||
if (selectedAnswer == '') { | ||
emit(QuizExerciseShow( | ||
quiz: quiz, | ||
quizExercise: currentProblem, | ||
remainingDuration: Duration(seconds: remainingDuration), | ||
selectedAnswer: selectedAnswer, | ||
modalErrorMessage: 'Pilih salah satu jawaban')); | ||
return; | ||
} | ||
try { | ||
var verdict = 'INCORRECT'; | ||
if (currentProblem.answer.correctAnswer.contains(selectedAnswer)) { | ||
verdict = 'CORRECT'; | ||
} | ||
attempt.answers?.add(QuizExerciseAnswer( | ||
answer: selectedAnswer, | ||
correctAnswer: currentProblem.answer.correctAnswer, | ||
taskChallengeGroup: currentProblem.challengeGroup, | ||
taskId: currentProblem.id, | ||
verdict: verdict)); | ||
|
||
if (verdict == 'CORRECT') { | ||
attempt.totalCorrect++; | ||
attempt.totalBlank--; | ||
} else { | ||
attempt.totalIncorrect++; | ||
attempt.totalBlank--; | ||
} | ||
|
||
currentProblemIndex++; | ||
|
||
if (currentProblemIndex < problemIdList.length) { | ||
currentProblem = await quizExerciseRepository | ||
.getQuizExercise(problemIdList[currentProblemIndex]); | ||
selectedAnswer = ''; | ||
emit(QuizExerciseShow( | ||
quiz: quiz, | ||
quizExercise: currentProblem, | ||
remainingDuration: Duration(seconds: remainingDuration), | ||
selectedAnswer: selectedAnswer, | ||
)); | ||
} else { | ||
attempt.score = (attempt.totalCorrect / | ||
(attempt.totalCorrect + | ||
attempt.totalIncorrect + | ||
attempt.totalBlank) * | ||
100) | ||
.toInt(); | ||
attempt.endAt = DateTime.now(); | ||
attempt.uploadedAt = DateTime.now(); | ||
await quizExerciseRepository.insertQuizExerciseAttempt( | ||
quizParticipantId, attempt); | ||
emit(QuizExerciseFinished(attempt)); | ||
} | ||
} catch (e) { | ||
emit(QuizExerciseFailed(e.toString())); | ||
} | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
app/lib/features/quiz_exercise/presentation/bloc/quiz_exercise_event.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
part of 'quiz_exercise_cubit.dart'; | ||
|
||
abstract class QuizExerciseEvent extends Equatable { | ||
const QuizExerciseEvent(); | ||
|
||
@override | ||
List<Object?> get props => []; | ||
} |
51 changes: 51 additions & 0 deletions
51
app/lib/features/quiz_exercise/presentation/bloc/quiz_exercise_state.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
part of 'quiz_exercise_cubit.dart'; | ||
|
||
abstract class QuizExerciseState extends Equatable { | ||
const QuizExerciseState(); | ||
|
||
@override | ||
List<Object> get props => []; | ||
} | ||
|
||
class QuizExerciseInitialState extends QuizExerciseState {} | ||
|
||
class QuizExerciseLoading extends QuizExerciseState {} | ||
|
||
class QuizExerciseShow extends QuizExerciseState { | ||
final QuizExercise quizExercise; | ||
final WeeklyQuiz quiz; | ||
final Duration remainingDuration; | ||
final String selectedAnswer; | ||
final String modalErrorMessage; | ||
const QuizExerciseShow( | ||
{required this.quiz, | ||
required this.quizExercise, | ||
required this.remainingDuration, | ||
required this.selectedAnswer, | ||
this.modalErrorMessage = ''}); | ||
|
||
@override | ||
List<Object> get props => [ | ||
quiz, | ||
quizExercise, | ||
remainingDuration, | ||
selectedAnswer, | ||
modalErrorMessage | ||
]; | ||
} | ||
|
||
class QuizExerciseFinished extends QuizExerciseState { | ||
final QuizExerciseAttempt quizExerciseAttempt; | ||
const QuizExerciseFinished(this.quizExerciseAttempt); | ||
|
||
@override | ||
List<Object> get props => [quizExerciseAttempt]; | ||
} | ||
|
||
class QuizExerciseFailed extends QuizExerciseState { | ||
final String error; | ||
|
||
const QuizExerciseFailed(this.error); | ||
@override | ||
List<Object> get props => [error]; | ||
} |
25 changes: 25 additions & 0 deletions
25
app/lib/features/quiz_exercise/presentation/model/answer.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import 'aspect.dart'; | ||
import 'explanation.dart'; | ||
|
||
class Answer { | ||
final Aspect aspect; | ||
final List<String> correctAnswer; | ||
final Explanation explanation; | ||
|
||
Answer({ | ||
required this.aspect, | ||
required this.correctAnswer, | ||
required this.explanation, | ||
}); | ||
|
||
factory Answer.fromJson(Map<String, dynamic> json) { | ||
return Answer( | ||
aspect: Aspect.fromJson(json['aspect'] as Map<String, dynamic>), | ||
correctAnswer: (json['correct_answer'] as List<dynamic>) | ||
.map((e) => e.toString()) | ||
.toList(), | ||
explanation: | ||
Explanation.fromJson(json['explanation'] as Map<String, dynamic>), | ||
); | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
app/lib/features/quiz_exercise/presentation/model/aspect.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
class Aspect { | ||
final String content; | ||
final String label; | ||
Aspect({required this.content, required this.label}); | ||
|
||
factory Aspect.fromJson(Map<String, dynamic> json) { | ||
return Aspect( | ||
content: json['content'] as String, | ||
label: json['label'] as String, | ||
); | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
app/lib/features/quiz_exercise/presentation/model/description.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
class Description { | ||
final String content; | ||
Description({required this.content}); | ||
|
||
factory Description.fromJson(Map<String, dynamic> json) { | ||
return Description(content: json['content'] as String); | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
app/lib/features/quiz_exercise/presentation/model/explanation.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
class Explanation { | ||
final String content; | ||
|
||
Explanation({required this.content}); | ||
|
||
factory Explanation.fromJson(Map<String, dynamic> json) { | ||
return Explanation( | ||
content: json['content'] as String, | ||
); | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
app/lib/features/quiz_exercise/presentation/model/options.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
class Options { | ||
final String content; | ||
final String id; | ||
Options({required this.content, required this.id}); | ||
|
||
factory Options.fromJson(Map<String, dynamic> json) { | ||
return Options( | ||
content: json['content'] as String, | ||
id: json['id'] as String, | ||
); | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
app/lib/features/quiz_exercise/presentation/model/questions.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import 'options.dart'; | ||
|
||
class Question { | ||
final String content; | ||
final List<Options> options; | ||
|
||
Question({required this.content, required this.options}); | ||
|
||
factory Question.fromJson(Map<String, dynamic> json) { | ||
return Question( | ||
content: json['content'] as String, | ||
options: (json['options'] as List<dynamic>) | ||
.map((e) => Options.fromJson(e as Map<String, dynamic>)) | ||
.toList(), | ||
); | ||
} | ||
} |
Oops, something went wrong.