Warning, file /education/parley/src/practice/writtenpracticevalidator.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2010 Benjamin Schleinzer <ben-kde@schleinzer.eu> 0003 SPDX-FileCopyrightText: 2007-2010 Frederik Gladhorn <gladhorn@kde.org> 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "writtenpracticevalidator.h" 0008 #include "prefs.h" 0009 0010 #include <KEduVocTranslation> 0011 #include <QDebug> 0012 0013 /// temporary namespace for string manipulation functions 0014 /// could move into KStringHandler eventually 0015 namespace ParleyStringHandlerOld 0016 { 0017 QString stripAccents(const QString &original) 0018 { 0019 QString noAccents; 0020 QString decomposed = original.normalized(QString::NormalizationForm_D); 0021 for (int i = 0; i < decomposed.length(); ++i) { 0022 if (decomposed[i].category() != 1) { 0023 noAccents.append(decomposed[i]); 0024 } 0025 } 0026 qDebug() << original << " without accents: " << noAccents; 0027 return noAccents; 0028 } 0029 } 0030 0031 using namespace Practice; 0032 0033 WrittenPracticeValidator::WrittenPracticeValidator(int translation, KEduVocDocument *doc) 0034 : m_doc(doc) 0035 , m_error() 0036 { 0037 setLanguage(translation); 0038 } 0039 0040 WrittenPracticeValidator::~WrittenPracticeValidator() 0041 { 0042 delete m_speller; 0043 } 0044 0045 void WrittenPracticeValidator::setEntry(TestEntry *entry) 0046 { 0047 m_entry = entry; 0048 } 0049 0050 void WrittenPracticeValidator::setLanguage(int translation) 0051 { 0052 m_translation = translation; 0053 0054 // default: try locale 0055 if (!m_speller) { 0056 m_speller = new Sonnet::Speller(m_doc->identifier(translation).locale()); 0057 } else { 0058 m_speller->setLanguage(m_doc->identifier(translation).locale()); 0059 } 0060 0061 // we might succeed with language name instead. 0062 if (!m_speller->isValid()) { 0063 m_speller->setLanguage(m_doc->identifier(translation).name()); 0064 } 0065 0066 if (!m_speller->isValid()) { 0067 qDebug() << "No spellchecker for current language found: " << m_doc->identifier(m_translation).locale(); 0068 qDebug() << "Available dictionaries: " << m_speller->availableLanguages() << "\n names: " << m_speller->availableLanguageNames() 0069 << "\n backends: " << m_speller->availableBackends(); 0070 m_spellerAvailable = false; 0071 } else { 0072 m_spellerAvailable = true; 0073 } 0074 } 0075 0076 bool WrittenPracticeValidator::spellcheckerAvailable() 0077 { 0078 return m_spellerAvailable; 0079 } 0080 0081 void WrittenPracticeValidator::validateAnswer(const QString &answer) 0082 { 0083 if (m_entry == 0) { 0084 qCritical() << "No entry set, cannot verify answer."; 0085 return; 0086 } 0087 0088 QString correct = m_entry->entry()->translation(m_entry->languageTo())->text(); 0089 0090 qDebug() << "Correct answer should be: " << correct; 0091 m_error = {}; 0092 0093 // Check for empty answers and valid answers first 0094 if (answer.isEmpty()) { 0095 m_error |= TestEntry::Wrong; 0096 qDebug() << "Empty answer "; 0097 } else if (isCorrect(correct, answer)) { 0098 m_error |= TestEntry::Correct; 0099 } else { 0100 // Check for all valid errors to build a list of 0101 // possible mistakes. This provides us with useful information 0102 // that we can use to give feedback to the user. 0103 if (isPunctuationMistake(correct, answer)) { 0104 m_error |= TestEntry::Correct; 0105 } else if (isCapitalizationMistake(correct, answer)) { 0106 m_error |= TestEntry::Correct; 0107 } else if (isAccentMistake(correct, answer)) { 0108 m_error |= TestEntry::Correct; 0109 } else if (isSynonymMistake(answer)) { 0110 m_error |= TestEntry::Correct; 0111 } else { 0112 m_error |= TestEntry::Wrong; 0113 qDebug() << "Wrong answer: " << answer; 0114 } 0115 } 0116 qDebug() << "Error code " << m_error; 0117 0118 m_entry->setLastErrors(m_error); 0119 } 0120 0121 QString WrittenPracticeValidator::getCorrectedAnswer() 0122 { 0123 return m_correctedAnswer; 0124 } 0125 0126 bool WrittenPracticeValidator::isCorrect(const QString &correct, const QString &answer) 0127 { 0128 if (answer == correct) { 0129 qDebug() << "Correct answer was given"; 0130 return true; 0131 } 0132 return false; 0133 } 0134 0135 bool WrittenPracticeValidator::isSynonymMistake(const QString &answer) 0136 { 0137 const QList<KEduVocTranslation *> synonyms = m_entry->entry()->translation(m_entry->languageTo())->synonyms(); 0138 for (KEduVocTranslation *synonym : synonyms) { 0139 if (synonym->text() == answer || (Prefs::ignoreCapitalizationMistakes() && isCapitalizationMistake(synonym->text(), answer)) 0140 || (Prefs::ignoreAccentMistakes() && isAccentMistake(synonym->text(), answer)) 0141 || (Prefs::ignorePunctuationMistakes() && isPunctuationMistake(synonym->text(), answer))) { 0142 qDebug() << "Synonym entered: " << synonym->text() << " answer: " << answer; 0143 m_correctedAnswer = synonym->text(); 0144 m_error |= TestEntry::Synonym; 0145 // only return true if accept these kinds of mistakes 0146 // otherwise just set the error flag 0147 if (Prefs::countSynonymsAsCorrect()) { 0148 return true; 0149 } 0150 } 0151 } 0152 return false; 0153 } 0154 0155 bool WrittenPracticeValidator::isCapitalizationMistake(const QString &original, const QString &answer) 0156 { 0157 if (answer.toLower() == original.toLower() || (Prefs::ignorePunctuationMistakes() && isPunctuationMistake(original.toLower(), answer.toLower()))) { 0158 qDebug() << "CapitalizationMistake: " << original << " answer: " << answer; 0159 m_error |= TestEntry::CapitalizationMistake; 0160 m_correctedAnswer = answer; 0161 // only return true if accept these kinds of mistakes 0162 // otherwise just set the error flag 0163 if (Prefs::ignoreCapitalizationMistakes()) 0164 return true; 0165 } 0166 return false; 0167 } 0168 0169 bool WrittenPracticeValidator::isPunctuationMistake(const QString &original, const QString &answer) 0170 { 0171 QString ans = answer; 0172 QString orig = original; 0173 if (ans.remove(QRegExp(QStringLiteral("[^a-zA-ZƒŠŒŽšœžŸÀ-ÿ\\s]"))) == orig.remove(QRegExp(QStringLiteral("[^a-zA-ZƒŠŒŽšœžŸÀ-ÿ\\s]")))) { 0174 qDebug() << "PunctuationMistake: " << original << " answer: " << answer; 0175 m_error |= TestEntry::PunctuationMistake; 0176 m_correctedAnswer = answer; 0177 // only return true if accept these kinds of mistakes 0178 // otherwise just set the error flag 0179 if (Prefs::ignorePunctuationMistakes()) 0180 return true; 0181 } 0182 return false; 0183 } 0184 0185 bool WrittenPracticeValidator::isAccentMistake(const QString &original, const QString &answer) 0186 { 0187 QString stripedOriginal = ParleyStringHandlerOld::stripAccents(original); 0188 QString stripedAnswer = ParleyStringHandlerOld::stripAccents(answer); 0189 if (stripedOriginal == stripedAnswer || (Prefs::ignoreCapitalizationMistakes() && isCapitalizationMistake(stripedOriginal, stripedAnswer)) 0190 || (Prefs::ignorePunctuationMistakes() && isPunctuationMistake(stripedOriginal, stripedAnswer))) { 0191 qDebug() << "AccentMistake: " << original << " answer: " << answer; 0192 m_error |= TestEntry::AccentMistake; 0193 m_correctedAnswer = answer; 0194 // only return true if accept these kinds of mistakes 0195 // otherwise just set the error flag 0196 if (Prefs::ignoreAccentMistakes()) { 0197 return true; 0198 } 0199 } 0200 return false; 0201 }