Skip to content

Commit

Permalink
Merge pull request #22 from ia-toki/18-task-quiz-make-presentation-of…
Browse files Browse the repository at this point in the history
…-quiz-exercise

quiz exercise
  • Loading branch information
gmochid authored Nov 2, 2023
2 parents 0ed80e9 + ddd5b5e commit b035e9a
Show file tree
Hide file tree
Showing 28 changed files with 850 additions and 42 deletions.
4 changes: 3 additions & 1 deletion app/lib/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import 'package:google_fonts/google_fonts.dart';

import 'features/authentication/register/bloc/user_register_bloc.dart';
import 'features/onboarding/presentation/bloc/user_initialization_bloc.dart';
import 'features/quiz_registration/bloc/quiz_registration_cubit.dart';
import 'features/quiz_exercise/presentation/bloc/quiz_exercise_cubit.dart';
import 'features/quiz_registration/presentation/bloc/quiz_registration_cubit.dart';
import 'services/di.dart';
import 'services/router_service.dart';

Expand All @@ -26,6 +27,7 @@ class App extends StatelessWidget {
OnboardingAuthEvent(),
),
),
BlocProvider(create: (context) => QuizExerciseCubit()),
BlocProvider(create: (context) => QuizRegistrationCubit()),
BlocProvider(create: (context) => get<UserRegisterBloc>()),
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,7 @@ class _MaterialMenuState extends State<MaterialMenu> {
icon: const Icon(
Icons.print_rounded,
size: 36,
color: Colors
.blue,
color: Colors.blue,
),
iconSize: 36,
)
Expand Down
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()));
}
}
}
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 => [];
}
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 app/lib/features/quiz_exercise/presentation/model/answer.dart
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 app/lib/features/quiz_exercise/presentation/model/aspect.dart
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,
);
}
}
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 app/lib/features/quiz_exercise/presentation/model/explanation.dart
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 app/lib/features/quiz_exercise/presentation/model/options.dart
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 app/lib/features/quiz_exercise/presentation/model/questions.dart
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(),
);
}
}
Loading

0 comments on commit b035e9a

Please sign in to comment.