1 /*
2 SPDX-FileCopyrightText: 2007-2009 Frederik Gladhorn <gladhorn@kde.org>
3 SPDX-License-Identifier: GPL-2.0-or-later
4 */
5
6 #include "practicesummarycomponent.h"
7 #include "prefs.h"
8
9 #include "parleyactions.h"
10
11 #include <KActionCollection>
12 #include <KColorScheme>
13 #include <KConfigGroup>
14 #include <KEduVocExpression>
15 #include <KLocalizedString>
16 #include <KMessageBox>
17 #include <KToolBar>
18 #include <QFileDialog>
19 #include <QTableWidgetItem>
20 #include <QTextCursor>
21 #include <QTextDocument>
22 #include <QTextDocumentWriter>
23 #include <QTextTable>
24
25 using namespace Practice;
26
27 class PracticeSummaryComponent::SortedAttemptTableWidgetItem : public QTableWidgetItem
28 {
operator <(const QTableWidgetItem & other) const29 bool operator<(const QTableWidgetItem &other) const override
30 {
31 if (data(Qt::DisplayRole).toInt() == other.data(Qt::DisplayRole).toInt()) {
32 return data(Qt::UserRole).toInt() < other.data(Qt::UserRole).toInt();
33 }
34 return data(Qt::DisplayRole).toInt() < other.data(Qt::DisplayRole).toInt();
35 }
36 };
37
PracticeSummaryComponent(SessionManagerBase * sessionManager,QWidget * parent)38 PracticeSummaryComponent::PracticeSummaryComponent(SessionManagerBase *sessionManager, QWidget *parent)
39 : KXmlGuiWindow(parent)
40 , m_sessionManager(sessionManager)
41 {
42 // KXmlGui
43 setXMLFile(QStringLiteral("practicesummaryui.rc"));
44 setObjectName(QStringLiteral("Statistics"));
45
46 QWidget *mainWidget = new QWidget(this);
47 setupUi(mainWidget);
48 setCentralWidget(mainWidget);
49
50 initActions(parent);
51
52 setupDetailsTable();
53 summaryBar->setStatistics(m_sessionManager->statisticTotalCorrectFirstAttempt(),
54 m_sessionManager->statisticTotalWrong(),
55 m_sessionManager->statisticTotalUnanswered());
56
57 int total = m_sessionManager->statisticTotalCorrectFirstAttempt() + m_sessionManager->statisticTotalWrong();
58 int minutes = m_sessionManager->totalTime() / 60;
59 int seconds = m_sessionManager->totalTime() % 60;
60
61 testSummaryLabel->setText(i18nc("number of words, minutes, seconds",
62 "You practiced %1 in %2 and %3.",
63 i18np("one word", "%1 words", total),
64 i18np("one minute", "%1 minutes", minutes),
65 i18np("one second", "%1 seconds", seconds)));
66
67 KConfigGroup cfg(KSharedConfig::openConfig(QStringLiteral("parleyrc")), objectName());
68 applyMainWindowSettings(cfg);
69 }
70
~PracticeSummaryComponent()71 PracticeSummaryComponent::~PracticeSummaryComponent()
72 {
73 KConfigGroup cfg(KSharedConfig::openConfig(QStringLiteral("parleyrc")), objectName());
74 saveMainWindowSettings(cfg);
75 }
76
initActions(QWidget * parleyMainWindow)77 void PracticeSummaryComponent::initActions(QWidget *parleyMainWindow)
78 {
79 ParleyActions::create(ParleyActions::EnterEditMode, parleyMainWindow, SLOT(showEditor()), actionCollection());
80 ParleyActions::create(ParleyActions::StartPractice, parleyMainWindow, SLOT(showPracticeConfiguration()), actionCollection());
81 ParleyActions::create(ParleyActions::ExportPracticeResults, this, SLOT(exportResults()), actionCollection());
82 actionCollection()->action(QStringLiteral("practice_start"))->setText(i18n("Practice Overview"));
83 actionCollection()->action(QStringLiteral("practice_start"))->setToolTip(i18n("Switch to the Practice Overview page"));
84 }
85
setupDetailsTable()86 void PracticeSummaryComponent::setupDetailsTable()
87 {
88 tableWidget->setRowCount(m_sessionManager->allEntryCount());
89 tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
90
91 Qt::ItemFlags flags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
92
93 KColorScheme scheme(QPalette::Active);
94 QPalette correctPalette = QApplication::palette();
95 correctPalette.setColor(QPalette::WindowText, scheme.foreground(KColorScheme::PositiveText).color());
96 correctPalette.setColor(QPalette::Text, scheme.foreground(KColorScheme::PositiveText).color());
97
98 QPalette wrongPalette = QApplication::palette();
99 wrongPalette.setColor(QPalette::WindowText, scheme.foreground(KColorScheme::NegativeText).color());
100 wrongPalette.setColor(QPalette::Text, scheme.foreground(KColorScheme::NegativeText).color());
101
102 int i = 0;
103 // TODO headers with languages
104 // TODO some colors, maybe an indicator icon whether the word was right/wrong
105 const QList<TestEntry *> allTestEntries = m_sessionManager->allTestEntries();
106 for (TestEntry *entry : allTestEntries) {
107 QTableWidgetItem *itemFrom = new QTableWidgetItem(entry->entry()->translation(entry->TestEntry::languageFrom())->text());
108 QTableWidgetItem *itemTo = new QTableWidgetItem(entry->entry()->translation(entry->languageTo())->text());
109 if (entry->statisticGoodCount() > 0) {
110 itemTo->setForeground(correctPalette.windowText());
111 }
112
113 QTableWidgetItem *itemUserAnswer = new QTableWidgetItem(entry->userAnswers().join(QStringLiteral("; ")));
114 itemUserAnswer->setForeground(wrongPalette.windowText());
115
116 SortedAttemptTableWidgetItem *itemAttempts = new SortedAttemptTableWidgetItem();
117 itemAttempts->setData(Qt::DisplayRole, entry->statisticCount());
118 itemAttempts->setData(Qt::UserRole, entry->statisticBadCount());
119 itemAttempts->setTextAlignment(Qt::AlignRight);
120
121 itemFrom->setFlags(flags);
122 itemTo->setFlags(flags);
123 itemUserAnswer->setFlags(flags);
124 itemAttempts->setFlags(flags);
125
126 if (entry->correctAtFirstAttempt()) {
127 itemUserAnswer->setIcon(QIcon::fromTheme(QStringLiteral("dialog-ok-apply")));
128 } else if (entry->statisticGoodCount() > 0) {
129 itemUserAnswer->setIcon(QIcon::fromTheme(QStringLiteral("task-attempt")));
130 } else if (entry->statisticCount() > 0) {
131 itemUserAnswer->setIcon(QIcon::fromTheme(QStringLiteral("dialog-error")));
132 } else {
133 itemUserAnswer->setIcon(QIcon::fromTheme(QStringLiteral("task-attempt")));
134 }
135
136 tableWidget->setItem(i, 0, itemAttempts);
137 tableWidget->setItem(i, 1, itemFrom);
138 tableWidget->setItem(i, 2, itemTo);
139 tableWidget->setItem(i, 3, itemUserAnswer);
140 ++i;
141 }
142
143 tableWidget->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents);
144 tableWidget->setSortingEnabled(true);
145 tableWidget->sortItems(0, Qt::DescendingOrder);
146 }
147
exportResults()148 void PracticeSummaryComponent::exportResults()
149 {
150 QString filter = i18n("HTML Files") + " (*.html);;" + i18n("OpenDocument text files") + " (*.odt)";
151 QString caption;
152 QString startingdir(QStringLiteral("kfiledialog:///practice_export"));
153 QString fileName = QFileDialog::getSaveFileName(0, caption, startingdir, filter);
154
155 if (fileName.isEmpty()) {
156 return;
157 }
158
159 QTextDocument doc;
160 doc.setHtml("<html><head><title>" + i18n("Practice results") + "</title></body></html>");
161 QTextCursor cursor(&doc);
162
163 cursor.insertHtml("<h1>" + m_sessionManager->title() + "</h1><br />");
164
165 cursor.insertText(i18n("Answered questions: %1\n", m_sessionManager->allEntryCount()));
166 cursor.insertText(i18n("Correct answers: %1\n", m_sessionManager->statisticTotalCorrectFirstAttempt()));
167 cursor.insertText(i18n("Wrong answers: %1\n", m_sessionManager->statisticTotalWrong()));
168
169 QTextTableFormat tableFormat;
170 tableFormat.setBorderStyle(QTextFrameFormat::BorderStyle_Solid);
171 tableFormat.setCellPadding(1);
172 tableFormat.setAlignment(Qt::AlignLeft);
173 QTextTable *table = cursor.insertTable(1, 4, tableFormat);
174 table->cellAt(0, 0).firstCursorPosition().insertHtml(i18n("<b>Attempts</b>"));
175 table->cellAt(0, 1).firstCursorPosition().insertHtml(i18n("<b>Question</b>"));
176 table->cellAt(0, 2).firstCursorPosition().insertHtml(i18n("<b>Correct answer</b>"));
177 table->cellAt(0, 3).firstCursorPosition().insertHtml(i18n("<b>Your errors</b>"));
178
179 const QList<TestEntry *> allTestEntries = m_sessionManager->allTestEntries();
180 for (TestEntry *entry : allTestEntries) {
181 table->appendRows(1);
182 int newRow = table->rows() - 1;
183 table->cellAt(newRow, 0).firstCursorPosition().insertText(QString::number(entry->statisticCount()));
184 table->cellAt(newRow, 1).firstCursorPosition().insertText(entry->entry()->translation(entry->languageFrom())->text());
185 table->cellAt(newRow, 2).firstCursorPosition().insertText(entry->entry()->translation(entry->languageTo())->text());
186 table->cellAt(newRow, 3).firstCursorPosition().insertText(entry->userAnswers().join(QStringLiteral("; ")));
187 }
188
189 QTextDocumentWriter writer(fileName);
190
191 if (!writer.write(&doc)) {
192 KMessageBox::error(this, i18n("Could not write to %1", fileName), i18n("Could not write file"));
193 return;
194 }
195 }
196