File indexing completed on 2023-05-30 10:45:26

0001 /*
0002     SPDX-FileCopyrightText: 2008-2010 Peter Hedlund <peter.hedlund@kdemail.net>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "kwqquizmodel.h"
0007 
0008 #include "blankanswer.h"
0009 #include "kwqsortfiltermodel.h"
0010 #include "kwqtablemodel.h"
0011 
0012 #include <QRandomGenerator>
0013 
0014 #include <KRandom>
0015 
0016 KWQQuizModel::KWQQuizModel(QObject *parent) : QSortFilterProxyModel(parent)
0017 {
0018     m_sourceModel = 0;
0019     m_currentQuestion = 0;
0020     setSortCaseSensitivity(Qt::CaseInsensitive);
0021     setFilterCaseSensitivity(Qt::CaseInsensitive);
0022     setFilterKeyColumn(-1);
0023     m_list.clear();
0024     m_errorList.clear();
0025     m_quizList.clear();
0026 }
0027 
0028 
0029 void KWQQuizModel::setFilterModel(KWQSortFilterModel * sourceModel)
0030 {
0031     m_sourceModel = sourceModel;
0032     QSortFilterProxyModel::setSourceModel(sourceModel);
0033 }
0034 
0035 KWQSortFilterModel * KWQQuizModel::sourceModel() const
0036 {
0037     return m_sourceModel;
0038 }
0039 
0040 bool KWQQuizModel::lessThan(const QModelIndex & left, const QModelIndex & right) const
0041 {
0042     return m_list[left.row()] < m_list[right.row()];
0043 }
0044 
0045 bool KWQQuizModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
0046 {
0047     QString text;
0048     text.clear();
0049     for (int i = 0 ; i < 2; i++)
0050         text.append(sourceModel()->data(sourceModel()->index(sourceRow, i, sourceParent), Qt::DisplayRole).toString());
0051 
0052     return !text.isEmpty();
0053 }
0054 
0055 
0056 int KWQQuizModel::column(int i)
0057 {
0058     int result  = 0;
0059     if (isOdd(m_quizMode))
0060         result = 1;
0061     if ((m_quizMode == 5) && qAbs(i) == i)
0062         result = 0;
0063     return result;
0064 }
0065 
0066 bool KWQQuizModel::atEnd()
0067 {
0068   return m_currentQuestion >= questionCount();
0069 }
0070 
0071 void KWQQuizModel::toNext()
0072 {
0073   ++m_currentQuestion;
0074 }
0075 
0076 bool KWQQuizModel::hasErrors()
0077 {
0078     if (m_quizType == Prefs::EnumStartSession::MultipleChoice)
0079         return m_errorList.count() > 2; //this is a hack until better multiple choice is written
0080     else
0081         return !m_errorList.isEmpty();
0082 }
0083 
0084 bool KWQQuizModel::init()
0085 {
0086     if (m_sourceModel == 0)
0087         return false;
0088 
0089     invalidate();
0090 
0091     //check if enough in list
0092     bool result = false;
0093     switch (m_quizType)  {
0094         case Prefs::EnumStartSession::Editor:
0095             //
0096             break;
0097         case Prefs::EnumStartSession::Flashcard:
0098             result = (rowCount(QModelIndex()) > 0);
0099             break;
0100         case Prefs::EnumStartSession::QA:
0101             result = (rowCount(QModelIndex()) > 0);
0102             break;
0103         case Prefs::EnumStartSession::MultipleChoice:
0104             result = (rowCount(QModelIndex()) > 2);
0105             break;
0106     }
0107 
0108     if (!result)
0109         return false;
0110 
0111     m_quizList.clear();
0112     for (int i = 0; i < rowCount(QModelIndex()); ++i) {
0113         m_quizList.append(i);
0114         if (m_quizMode == 5)
0115             m_quizList.append(-i);
0116     }
0117 
0118     activateBaseList();
0119     return true;
0120 }
0121 
0122 void KWQQuizModel::activateBaseList()
0123 {
0124     m_list.clear();
0125     m_errorList.clear();
0126     foreach(int l, m_quizList)
0127         m_list.append(l);
0128 
0129     if (m_quizMode > 2) {
0130         KRandom::shuffle(m_list, QRandomGenerator::global());
0131     };
0132 
0133     m_questionCount = m_list.count();
0134     m_currentQuestion = 0;
0135 }
0136 
0137 bool KWQQuizModel::checkAnswer(const QString & a)
0138 {
0139     bool result = false;
0140     QString ans = a;
0141 
0142     int row =  m_list.at(m_currentQuestion);
0143 
0144     QString tTemp = data(index(qAbs(row), column(row), QModelIndex()), Qt::DisplayRole).toString();
0145 
0146     tTemp = tTemp.simplified();
0147     ans = ans.simplified();
0148 
0149     if (m_quizType == Prefs::EnumStartSession::QA) {
0150         if (!m_correctBlank.isEmpty()) {
0151             QStringList la, ls;
0152             if (ans.indexOf(';') > 0)
0153                 ls = ans.split(';', Qt::SkipEmptyParts);
0154             else
0155                 ls.append(ans);
0156 
0157             if (m_correctBlank.indexOf(';') > 0)
0158                 la = m_correctBlank.split(';', Qt::SkipEmptyParts);
0159             else
0160                 la.append(m_correctBlank);
0161 
0162             result = (ls.count() == la.count());
0163             if (result) {
0164                 for (int counter = 0; counter < la.count(); counter++) {
0165                 result = (ls[counter].simplified() == la[counter].simplified());
0166                 if (!result)
0167                     break;
0168                 }
0169             }
0170         }
0171         else
0172         {
0173           result = (ans == tTemp);
0174         }
0175       }
0176       else
0177       {
0178         if (m_quizType == Prefs::EnumStartSession::MultipleChoice) {
0179             if (Prefs::enableBlanks()) {
0180                 tTemp.remove('[');
0181                 tTemp.remove(']');
0182             }
0183             result = (ans == tTemp);
0184         }
0185         else
0186         {
0187           result = (ans == tTemp);
0188         }
0189 
0190       }
0191       ///@todo currently the Leitner stuff crashes KWQ
0192       //QString tmpLeitner( m_doc->entry(li.oneOp())->leitnerBox() );
0193 
0194       if (!result) {
0195           //m_doc->entry(li.oneOp())->setLeitnerBox(m_doc->leitnerSystem()->wrongBox( tmpLeitner ));
0196           m_errorList.append(row);
0197       }
0198       //else
0199           //m_doc->entry(li.oneOp())->setLeitnerBox(m_doc->leitnerSystem()->correctBox( tmpLeitner ));
0200 
0201       return result;
0202 }
0203 
0204 
0205 QStringList KWQQuizModel::multiOptions()
0206 {
0207   QStringList ls;
0208   int a = -1;
0209   int b = -1;
0210 
0211   do
0212     a = QRandomGenerator::global()->bounded(m_questionCount);
0213   while(a == m_currentQuestion);
0214 
0215   do
0216     b = QRandomGenerator::global()->bounded(m_questionCount);
0217   while(b == m_currentQuestion || b == a);
0218 
0219   int row =  m_list.at(m_currentQuestion);
0220   int col  = column(row);
0221   if (col == 0)
0222     col = 0;
0223   else
0224     col = 1;
0225   ls.append(data(index(qAbs(row), col, QModelIndex()), Qt::DisplayRole).toString());
0226   row =  m_list.at(a);
0227   ls.append(data(index(qAbs(row), col, QModelIndex()), Qt::DisplayRole).toString());
0228   row =  m_list.at(b);
0229   ls.append(data(index(qAbs(row), col, QModelIndex()), Qt::DisplayRole).toString());
0230 
0231   if (Prefs::enableBlanks()) {
0232     for (int i = 0; i < ls.count(); i++) {
0233       ls[i].remove('[');
0234       ls[i].remove(']');
0235       }
0236   }
0237 
0238   KRandom::shuffle(ls, QRandomGenerator::global());
0239 
0240   return ls;
0241 }
0242 
0243 
0244 QString KWQQuizModel::quizIcon(QuizIcon ico)
0245 {
0246     QString s = QStringLiteral("question");
0247     int col = column(m_list.at(m_currentQuestion));
0248     if (ico == IconLeftCol) {
0249         if (col == 0)
0250             s = QStringLiteral("answer");
0251     }
0252 
0253     if (ico == IconRightCol) {
0254         if (col != 0)
0255             s = QStringLiteral("answer");
0256     }
0257     return s;
0258 }
0259 
0260 QString KWQQuizModel::yourAnswer(const QString &givenAnswer) const
0261 {
0262     return BlankAnswer::yourAnswerResult(givenAnswer, m_answerBlank);
0263 }
0264 
0265 
0266 QString KWQQuizModel::hint()
0267 {
0268     if (!m_correctBlank.isEmpty())
0269         return m_correctBlank;
0270     else
0271         return answer();
0272 }
0273 
0274 
0275 QString KWQQuizModel::question()
0276 {
0277     int row =  m_list.at(m_currentQuestion);
0278     int col  = column(row);
0279     if (col == 0)
0280         col = 1;
0281     else
0282         col = 0;
0283 
0284     QString s = data(index(qAbs(row), col, QModelIndex()), Qt::DisplayRole).toString();
0285 
0286     if (Prefs::enableBlanks()) {
0287         s.remove('[');
0288         s.remove(']');
0289     }
0290 
0291     if (m_quizType != Prefs::EnumStartSession::Flashcard && m_currentQuestion > 0)
0292         Q_EMIT checkingAnswer(m_list.at(m_currentQuestion - 1));
0293     else
0294         Q_EMIT checkingAnswer(row);
0295 
0296     return s;
0297 }
0298 
0299 QString KWQQuizModel::blankAnswer()
0300 {
0301     m_correctBlank.clear();
0302     m_answerBlank.clear();
0303 
0304     if (m_quizType == Prefs::EnumStartSession::QA && Prefs::enableBlanks()) {
0305         int row = m_list.at(m_currentQuestion);
0306         const QString input = data(index(qAbs(row), column(row), QModelIndex()), Qt::DisplayRole).toString();
0307 
0308         const BlankAnswer::BlankResult result = BlankAnswer::blankAnswer(input);
0309 
0310         m_answerBlank = result.blankedAnswer;
0311         m_correctBlank = result.correctAnswer;
0312     }
0313     return m_answerBlank;
0314 }
0315 
0316 QString KWQQuizModel::answer()
0317 {
0318     int row =  m_list.at(m_currentQuestion);
0319     QString s = data(index(qAbs(row), column(row), QModelIndex()), Qt::DisplayRole).toString();
0320 
0321     if (m_quizType == Prefs::EnumStartSession::QA) {
0322         if (Prefs::enableBlanks()) {
0323             s.replace('[', QLatin1String("<u>"));
0324             s.replace(']', QLatin1String("</u>"));
0325             s.prepend("<qt>");
0326             s.append("</qt>");
0327         }
0328     }
0329     else
0330     {
0331         if (Prefs::enableBlanks()) {
0332             s.remove('[');
0333             s.remove(']');
0334         }
0335     }
0336     return s;
0337 }
0338 
0339 
0340 QUrl KWQQuizModel::soundQuestion()
0341 {
0342     int row =  m_list.at(m_currentQuestion);
0343     int col = column(row);
0344     col == 0 ? col = 1 : col = 0;
0345  
0346     QUrl soundUrl = QUrl::fromLocalFile(data(index(qAbs(row), col, QModelIndex()), KWQTableModel::SoundRole).toString());
0347     return soundUrl;
0348 }
0349  
0350 
0351 QUrl KWQQuizModel::soundAnswer()
0352 {
0353     int row =  m_list.at(m_currentQuestion);
0354     QUrl soundUrl = QUrl::fromLocalFile(data(index(qAbs(row), column(row), QModelIndex()), KWQTableModel::SoundRole).toString());
0355     return soundUrl;
0356 }
0357 
0358 
0359 QPixmap KWQQuizModel::imageQuestion()
0360 {
0361     int row =  m_list.at(m_currentQuestion);
0362     int col = column(row);
0363     col == 0 ? col = 1 : col = 0;
0364 
0365     QUrl imageUrl = QUrl::fromLocalFile(data(index(qAbs(row), col, QModelIndex()), KWQTableModel::ImageRole).toString());
0366     return QPixmap(imageUrl.toLocalFile());
0367 }
0368 
0369 
0370 QPixmap KWQQuizModel::imageAnswer()
0371 {
0372     int row =  m_list.at(m_currentQuestion);
0373     QUrl imageUrl = QUrl::fromLocalFile(data(index(qAbs(row), column(row), QModelIndex()), KWQTableModel::ImageRole).toString());
0374     return QPixmap(imageUrl.toLocalFile());
0375 }
0376 
0377 
0378 QString KWQQuizModel::langQuestion()
0379 {
0380     if (column(m_list.at(m_currentQuestion)) == 1)
0381         return headerData(0, Qt::Horizontal, Qt::DisplayRole).toString();
0382     else
0383         return headerData(1, Qt::Horizontal, Qt::DisplayRole).toString();
0384 }
0385 
0386 
0387 QString KWQQuizModel::langAnswer()
0388 {
0389     if (column(m_list.at(m_currentQuestion)) == 0)
0390         return headerData(0, Qt::Horizontal, Qt::DisplayRole).toString();
0391     else
0392         return headerData(1, Qt::Horizontal, Qt::DisplayRole).toString();
0393 }
0394 
0395 
0396 QString KWQQuizModel::kbAnswer()
0397 {
0398     int col = column(m_list.at(m_currentQuestion));
0399     QString result;
0400 
0401     if (col == 0)
0402         return headerData(0, Qt::Horizontal, KWQTableModel::KeyboardLayoutRole).toString();
0403     if (col == 1)
0404         return headerData(1, Qt::Horizontal, KWQTableModel::KeyboardLayoutRole).toString();
0405 
0406     return result;
0407 }
0408 
0409 
0410 void KWQQuizModel::removeLastError()
0411 {
0412     m_errorList.removeLast();
0413 }
0414 
0415 
0416 int KWQQuizModel::questionCount()
0417 {
0418     return m_questionCount;
0419 }
0420 
0421 
0422 void KWQQuizModel::finish()
0423 {
0424     Q_EMIT checkingAnswer(-1);
0425 }
0426 
0427 
0428 void KWQQuizModel::setQuizType(Prefs::EnumStartSession::type qt)
0429 {
0430     m_quizType = qt;
0431 }
0432 
0433 
0434 void KWQQuizModel::setQuizMode(int qm)
0435 {
0436     m_quizMode = qm;
0437 }
0438 
0439 
0440 void KWQQuizModel::activateErrorList()
0441 {
0442     m_list.clear();
0443     foreach(int i, m_errorList)
0444         m_list.append(i);
0445 
0446     m_errorList.clear();
0447     m_questionCount = m_list.count();
0448     m_currentQuestion = 0;
0449 }
0450 
0451 inline bool KWQQuizModel::isOdd(int value) const { return value & 0x1; }