ilyalehchylin/educats-xamarin

View on GitHub
source/EduCATS/Pages/Testing/Passing/ViewModels/TestPassingPageViewModel.cs

Summary

Maintainability
C
1 day
Test Coverage
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EduCATS.Data;
using EduCATS.Data.Models;
using EduCATS.Data.User;
using EduCATS.Helpers.Extensions;
using EduCATS.Helpers.Date;
using EduCATS.Helpers.Forms;
using EduCATS.Helpers.Logs;
using EduCATS.Networking.Models.Testing;
using EduCATS.Pages.Testing.Passing.Models;
using EduCATS.Themes;
using Nyxbull.Plugins.CrossLocalization;
using Xamarin.Forms;
using EduCATS.Networking;

namespace EduCATS.Pages.Testing.Passing.ViewModels
{
    public partial class TestPassingPageViewModel : ViewModel
    {
        readonly IPlatformServices _services;
        readonly DateTime _startedEntireTest;
        readonly int _testId;

        bool _timerCancellation;
        int _timeForCompletion;
        bool _isBusySpeech;

        /// <summary>
        /// Time for question/entire test completion.
        /// </summary>
        /// <remarks>
        /// If <c>_isTimeForEntireTest</c> is set to <c>true</c>,
        /// the time will be in minutes, in seconds otherwise.
        /// </remarks>
        bool _isTimeForEntireTest;

        int _questionCount;
        int _questionsLeft;
        string _testIdString;
        int _questionNumber;
        int _questionType;
        DateTime _started;

        public TestPassingPageViewModel(IPlatformServices services, int testId, bool forSelfStudy)
        {
            _startedEntireTest = DateTime.Now;
            _services = services;
            _testId = testId;
            _testIdString = testId.ToString();
            IsTestForSelfStudy = forSelfStudy;
            HeadphonesIcon = Theme.Current.BaseHeadphonesIcon;
            getData(1);
        }

        bool _isNotLoading;
        public bool IsNotLoading {
            get { return _isNotLoading; }
            set { SetProperty(ref _isNotLoading, value); }
        }

        bool _isTestForSelfStudy;
        public bool IsTestForSelfStudy {
            get { return _isTestForSelfStudy; }
            set { SetProperty(ref _isTestForSelfStudy, value); }
        }

        object _selectedItem;
        public object SelectedItem {
            get { return _selectedItem; }
            set {
                SetProperty(ref _selectedItem, value);

                if (_selectedItem != null) {
                    selectItem(_selectedItem);
                }
            }
        }

        string _title;
        public string Title {
            get { return _title; }
            set { SetProperty(ref _title, value); }
        }

        string _question;
        public string Question {
            get { return _question; }
            set { SetProperty(ref _question, value); }
        }

        string _description;
        public string Description {
            get { return _description; }
            set { SetProperty(ref _description, value); }
        }

        string _headphonesIcon;
        public string HeadphonesIcon {
            get { return _headphonesIcon; }
            set { SetProperty(ref _headphonesIcon, value); }
        }

        List<TestPassingAnswerModel> _answers;
        public List<TestPassingAnswerModel> Answers {
            get { return _answers; }
            set { SetProperty(ref _answers, value); }
        }

        Command _answerCommand;
        public Command AnswerCommand {
            get {
                return _answerCommand ?? (_answerCommand = new Command(
                    async () => await ExecuteAnswerCommand()));
            }
        }

        Command _skipCommand;
        public Command SkipCommand {
            get {
                return _skipCommand ?? (_skipCommand = new Command(
                    async () => await ExecuteSkipCommand()));
            }
        }

        Command _speechCommand;
        public Command SpeechCommand {
            get {
                return _speechCommand ?? (_speechCommand = new Command(
                    async () => await speechToText()));
            }
        }

        Command _closeCommand;
        public Command CloseCommand {
            get {
                return _closeCommand ?? (_closeCommand = new Command(
                    async () => await closePage()));
            }
        }

        void getData(int number)
        {
            _services.Device.MainThread(async () => {
                try {
                    setLoading(true);
                    await getAndSetTest();
                    await getAndSetQuestion(number);
                    setTimer();
                    _questionsLeft = _questionCount - _questionNumber + 1;
                    setLoading(false);
                } catch (Exception ex) {
                    AppLogs.Log(ex);
                }
            });
        }

        async Task getAndSetTest()
        {
            var test = await getTest();
            setTestData(test);
        }

        async Task<TestDetailsModel> getTest()
        {
            var test = await DataAccess.GetTest(_testId);

            if (DataAccess.IsError) {
                _services.Dialogs.ShowError(DataAccess.ErrorMessage);
                return new TestDetailsModel();
            }

            return test;
        }

        async Task getAndSetQuestion(int number)
        {
            if (number == -1)
            {
                if (Servers.EduCatsBntuAddress == _services.Preferences.Server)
                {
                    await getQuestion(number);
                }
                completeTest();
            }
            else
            {
                var question = await getQuestion(number);

                if (question.Question == null)
                {
                    completeTest();
                }
                else
                {
                    setQuestionData(question);
                }
            }
        }

        async Task<TestQuestionModel> getQuestion(int number)
        {
            var question = await DataAccess.GetNextQuestion(
                _testId, number, AppUserData.UserId);

            if (DataAccess.IsError && !DataAccess.IsConnectionError) {
                _services.Dialogs.ShowError(DataAccess.ErrorMessage);
                return new TestQuestionModel();
            }

            return question;
        }

        async Task answerQuestion(TestAnswerPostModel answerModel, bool isAuto = false)
        {
            try {
                if (answerModel == null || answerModel.Answers == null || answerModel.Answers.Count == 0) {
                    _services.Dialogs.ShowError(CrossLocalization.Translate("answer_question_not_selected_error"));
                    return;
                }

                await DataAccess.AnswerQuestionAndGetNext(answerModel);

                if (DataAccess.IsError) {
                    _services.Dialogs.ShowError(DataAccess.ErrorMessage);
                    return;
                }

                if (!isAuto) {
                    _questionsLeft--;
                }

                await getAndSetQuestion(getNextQuestion());
            } catch (Exception ex) {
                AppLogs.Log(ex);
            }
        }

        void setQuestionData(TestQuestionModel testQuestionCommonModel)
        {
            var testQuestionModel = testQuestionCommonModel.Question;

            if (testQuestionModel != null) {
                Question = testQuestionModel.Title;
                Description = $"<head><meta charset=\"utf-8\">" +
                    $"<font size=\"5\" " +
                    $"color=\"{Theme.Current.TestPassingQuestionColor}\">" +
                    $"{testQuestionModel.Description}" +
                    $"</font>";
                _questionNumber = testQuestionCommonModel.Number;
                _questionType = testQuestionModel.QuestionType;
                setAnswers(testQuestionModel.Answers);
            }
        }

        void setAnswers(List<TestAnswerModel> testQuestionAnswers)
        {
            var answersList = testQuestionAnswers?.Select(
                a => new TestPassingAnswerModel(a, _questionType) {
                    DownMovableAnswerCommand = new Command(ExecuteDownMovableAnswerCommand),
                    UpMovableAnswerCommand = new Command(ExecuteUpMovableAnswerCommand)
                }).ToList();

            if (_questionType < 2 && answersList.Count > 0) {
                answersList[0].IsSelected = true;
            }

            Answers = new List<TestPassingAnswerModel>(answersList);
        }

        void setTitle(TimeSpan timeLeft)
        {
            var questionDetails = $"{_questionNumber}/{_questionCount}";
            var timeLeftFormatted = timeLeft.ToString(@"hh\:mm\:ss");
            Title = $"{CrossLocalization.Translate("question_title")} {questionDetails} ({timeLeftFormatted})";
        }

        void completeTest()
        {
            var testTimePassed = DateHelper.CheckDatesDifference(_startedEntireTest, DateTime.Now);
            _timerCancellation = true;
            _services.Navigation.OpenTestResults(_testId, false, testTimePassed.ToString(@"hh\:mm\:ss"));
        }

        void autoAnswerAllQuestions()
        {
            _services.Device.MainThread(async () => {
                for (int i = 0; i < _questionsLeft; i++) {
                    await answerQuestion(true);
                }
            });
        }

        void completeQuestion()
        {
            _services.Device.MainThread(async () => {
                await answerQuestion(true);
                _started = DateTime.Now;
            });
        }

        void selectItem(object item)
        {
            try {
                if (item == null || item.GetType() != typeof(TestPassingAnswerModel)) {
                    return;
                }

                var answer = item as TestPassingAnswerModel;
                var answers = new List<TestPassingAnswerModel>(Answers);

                switch (_questionType) {
                    case 0:
                        answers.ForEach(a => a.IsSelected = a == answer ? true : false);
                        break;
                    case 1:
                        var index = answers.IndexOf(answer);
                        answers[index].IsSelected = !answers[index].IsSelected;
                        break;
                }

                Answers = new List<TestPassingAnswerModel>(answers);
            } catch (Exception ex) {
                AppLogs.Log(ex);
            }
        }

        protected async Task speechToText()
        {
            try {
                if (Answers == null || string.IsNullOrEmpty(Question)) {
                    return;
                }

                if (_isBusySpeech) {
                    _isBusySpeech = false;
                    _services.Device.CancelSpeech();
                    HeadphonesIcon = Theme.Current.BaseHeadphonesIcon;
                    return;
                }

                HeadphonesIcon = Theme.Current.BaseHeadphonesCancelIcon;
                _isBusySpeech = true;
                await _services.Device.Speak(Question);

                if (!_isBusySpeech) {
                    return;
                }

                var formattedDescription = Description.RemoveHTMLTags();

                if (!string.IsNullOrEmpty(formattedDescription)) {
                    await _services.Device.Speak(formattedDescription);
                }

                if (!_isBusySpeech) {
                    return;
                }

                await _services.Device.Speak(CrossLocalization.Translate("test_passing_options"));

                if (!_isBusySpeech) {
                    return;
                }

                for (var i = 0; i < Answers.Count; i++) {
                    var answer = Answers[i];

                    if (!_isBusySpeech) {
                        return;
                    }

                    await _services.Device.Speak((i + 1).ToString());

                    if (!_isBusySpeech) {
                        return;
                    }

                    if (!string.IsNullOrEmpty(answer.Content)) {
                        await _services.Device.Speak(answer.Content);
                    }
                }

                _isBusySpeech = false;
                HeadphonesIcon = Theme.Current.BaseHeadphonesIcon;
            } catch (Exception ex) {
                AppLogs.Log(ex);
            }
        }

        protected async Task ExecuteAnswerCommand()
        {
            await answerQuestion();

            if (!_isTimeForEntireTest) {
                _started = DateTime.Now;
            }
        }

        protected void ExecuteUpMovableAnswerCommand(object obj)
        {
            moveAnswer(obj, false);
        }

        protected void ExecuteDownMovableAnswerCommand(object obj)
        {
            moveAnswer(obj, true);
        }

        protected async Task ExecuteSkipCommand()
        {
            try {
                setLoading(true);
                await getAndSetQuestion(getNextQuestion());

                if (!_isTimeForEntireTest) {
                    _started = DateTime.Now;
                }

                setLoading(false);
            } catch (Exception ex) {
                AppLogs.Log(ex);
            }
        }

        protected async Task closePage()
        {
            try {
                var result = await _services.Dialogs.ShowConfirmationMessage(
                    CrossLocalization.Translate("base_warning"),
                    CrossLocalization.Translate("test_passing_cancel_message"));

                if (!result) {
                    return;
                }

                _timerCancellation = true;

                if (_isBusySpeech) {
                    _isBusySpeech = false;
                    _services.Device.CancelSpeech();
                }

                await _services.Navigation.ClosePage(true, false);
            } catch (Exception ex) {
                AppLogs.Log(ex);
            }
        }

        void setLoading(bool loading)
        {
            _services.Device.MainThread(() => {
                if (loading) {
                    _services.Dialogs.ShowLoading();
                } else {
                    _services.Dialogs.HideLoading();
                }

                IsNotLoading = !loading;
            });
        }
    }
}