File indexing completed on 2024-09-29 03:29:23

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