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