mirror of
https://github.com/jorenchik/mdemory.git
synced 2026-03-22 00:26:21 +00:00
spacing with correct cooldowns and updates
This commit is contained in:
14
src/include/main.h
Normal file
14
src/include/main.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "parser.h"
|
||||||
|
|
||||||
|
void update();
|
||||||
|
void saveMdem();
|
||||||
|
|
||||||
|
struct MdemBuffer {
|
||||||
|
std::vector<Question*> questions = std::vector<Question*>();
|
||||||
|
time_t trainedAt = 0;
|
||||||
|
};
|
||||||
|
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
|
|
||||||
extern QMainWindow *trainWindow;
|
extern QMainWindow *trainWindow;
|
||||||
@@ -15,7 +16,6 @@ enum PracticeAlgorithm {
|
|||||||
void initTrainWindow();
|
void initTrainWindow();
|
||||||
|
|
||||||
void initiatePractice(
|
void initiatePractice(
|
||||||
std::vector<Question*> questions,
|
MdemBuffer *mdemBuffer,
|
||||||
PracticeAlgorithm algorithm,
|
PracticeAlgorithm algorithm
|
||||||
time_t *trainedAt
|
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -58,6 +58,7 @@
|
|||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
#include "api.h"
|
#include "api.h"
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
#include "qscilexer.h"
|
#include "qscilexer.h"
|
||||||
@@ -86,11 +87,6 @@ struct Mdem {
|
|||||||
Question *question;
|
Question *question;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MdemBuffer {
|
|
||||||
std::vector<Question*> questions = std::vector<Question*>();
|
|
||||||
time_t trainedAt = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ErrorView {
|
struct ErrorView {
|
||||||
QWidget box;
|
QWidget box;
|
||||||
QVBoxLayout layout;
|
QVBoxLayout layout;
|
||||||
@@ -164,6 +160,8 @@ std::string outputMdem(std::vector<Question*> questions, time_t time = 0) {
|
|||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
|
||||||
if (time > 0) {
|
if (time > 0) {
|
||||||
|
auto timezoneOffset = settings->value("timezone").toInt();
|
||||||
|
/*time = time + 3600 * timezoneOffset;*/
|
||||||
std::tm* tm = std::localtime(&time);
|
std::tm* tm = std::localtime(&time);
|
||||||
char buffer[100];
|
char buffer[100];
|
||||||
std::strftime(buffer, sizeof(buffer), "%d.%m.%Y %H\\:%M", tm);
|
std::strftime(buffer, sizeof(buffer), "%d.%m.%Y %H\\:%M", tm);
|
||||||
@@ -242,8 +240,13 @@ void makePages() {
|
|||||||
|
|
||||||
void setupMdem(Mdem *mdem, Question *question) {
|
void setupMdem(Mdem *mdem, Question *question) {
|
||||||
if (MultiElementQuestion* mw = dynamic_cast<MultiElementQuestion*>(question)) {
|
if (MultiElementQuestion* mw = dynamic_cast<MultiElementQuestion*>(question)) {
|
||||||
|
std::stringstream ss;
|
||||||
|
if (mw->Cooldown > 0) {
|
||||||
|
ss << std::format("[{:.2f}] ", question->Cooldown);
|
||||||
|
}
|
||||||
|
ss << mw->QuestionText;
|
||||||
mdem->wFrontText.setText(
|
mdem->wFrontText.setText(
|
||||||
QString::fromStdString(mw->QuestionText)
|
QString::fromStdString(ss.str())
|
||||||
);
|
);
|
||||||
auto choices = mw->Choices;
|
auto choices = mw->Choices;
|
||||||
for (size_t k = 0; k < choices.size(); ++k) {
|
for (size_t k = 0; k < choices.size(); ++k) {
|
||||||
@@ -444,7 +447,35 @@ void CreateMdems(std::vector<Question*>& questions) {
|
|||||||
hMdemScroll->addItem(mdemSpacer);
|
hMdemScroll->addItem(mdemSpacer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateMdemInfo(std::string filename, bool isChanged = false) {
|
||||||
|
if (filename.length() > 0) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << std::format("mdem: {}", filename);
|
||||||
|
if (isChanged) {
|
||||||
|
ss << "*";
|
||||||
|
}
|
||||||
|
if (mdemBuffer->trainedAt > 0) {
|
||||||
|
std::tm* tm = std::localtime(&mdemBuffer->trainedAt);
|
||||||
|
char buffer[100];
|
||||||
|
std::strftime(buffer, sizeof(buffer), "%d.%m.%Y %H:%M", tm);
|
||||||
|
ss << std::endl << "Last practiced: " << std::string(buffer);
|
||||||
|
}
|
||||||
|
deckListLabel->setText(QString::fromStdString(ss.str()));
|
||||||
|
} else {
|
||||||
|
deckListLabel->setText("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getFilename(std::string path) {
|
||||||
|
std::smatch matches;
|
||||||
|
auto filenameMatched = std::regex_search(path, matches, lastPathElementExp);
|
||||||
|
return matches[2].str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void update() {
|
||||||
|
SwitchPage(currentPage);
|
||||||
|
updateMdemInfo(getFilename(currentMdem));
|
||||||
|
}
|
||||||
|
|
||||||
void SwitchPage(int pageIdx) {
|
void SwitchPage(int pageIdx) {
|
||||||
currentPage = pageIdx;
|
currentPage = pageIdx;
|
||||||
@@ -560,29 +591,6 @@ void releaseError(ErrorView** item) {
|
|||||||
std::cout << std::format("Released, current pool size: {}\n", errorPool.size());
|
std::cout << std::format("Released, current pool size: {}\n", errorPool.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getFilename(std::string path) {
|
|
||||||
std::smatch matches;
|
|
||||||
auto filenameMatched = std::regex_search(path, matches, lastPathElementExp);
|
|
||||||
return matches[2].str();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void updateMdemInfo(std::string filename) {
|
|
||||||
if (filename.length() > 0) {
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << std::format("mdem: {}", filename);
|
|
||||||
if (mdemBuffer->trainedAt > 0) {
|
|
||||||
std::tm* tm = std::localtime(&mdemBuffer->trainedAt);
|
|
||||||
char buffer[100];
|
|
||||||
std::strftime(buffer, sizeof(buffer), "%d.%m.%Y %H:%M", tm);
|
|
||||||
ss << std::endl << "Last practiced: " << std::string(buffer);
|
|
||||||
}
|
|
||||||
deckListLabel->setText(QString::fromStdString(ss.str()));
|
|
||||||
} else {
|
|
||||||
deckListLabel->setText("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void reloadMdem() {
|
void reloadMdem() {
|
||||||
auto file = std::ifstream(currentMdem);
|
auto file = std::ifstream(currentMdem);
|
||||||
std::string content;
|
std::string content;
|
||||||
@@ -616,7 +624,7 @@ void reloadMdem() {
|
|||||||
mdemBuffer->trainedAt = 0;
|
mdemBuffer->trainedAt = 0;
|
||||||
} else {
|
} else {
|
||||||
auto timezoneOffset = settings->value("timezone").toInt();
|
auto timezoneOffset = settings->value("timezone").toInt();
|
||||||
mdemBuffer->trainedAt = res.value.lastTrainedAt + 3600 * timezoneOffset;
|
mdemBuffer->trainedAt = res.value.lastTrainedAt - 3600 * timezoneOffset;
|
||||||
}
|
}
|
||||||
std::cout << std::format("Last trained at: {}", mdemBuffer->trainedAt) << std::endl;
|
std::cout << std::format("Last trained at: {}", mdemBuffer->trainedAt) << std::endl;
|
||||||
|
|
||||||
@@ -705,13 +713,13 @@ void setupEditorSave(bool checked) {
|
|||||||
for (int i = 0; i < mdemBuffer->questions.size(); ++i) {
|
for (int i = 0; i < mdemBuffer->questions.size(); ++i) {
|
||||||
if (mdemBuffer->questions[i] == oldQuestion) {
|
if (mdemBuffer->questions[i] == oldQuestion) {
|
||||||
mdemBuffer->questions.erase(mdemBuffer->questions.begin() + i);
|
mdemBuffer->questions.erase(mdemBuffer->questions.begin() + i);
|
||||||
delete mdemBuffer->questions[i];
|
delete oldQuestion;
|
||||||
mdemBuffer->questions[i] = editMdem->question;
|
mdemBuffer->questions.insert(mdemBuffer->questions.begin() + i, res.value.questions[0]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*setupMdem(editMdem, res.value.questions[0]);*/
|
setupMdem(editMdem, res.value.questions[0]);
|
||||||
/*showBacklabels(editMdem);*/
|
showBacklabels(editMdem);
|
||||||
editorWindow->hide();
|
editorWindow->hide();
|
||||||
} else {
|
} else {
|
||||||
QMessageBox::information(
|
QMessageBox::information(
|
||||||
@@ -741,6 +749,12 @@ void setupEditorSave(bool checked) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void saveMdem() {
|
||||||
|
auto filename = getFilename(currentMdem);
|
||||||
|
std::ofstream out(currentMdem);
|
||||||
|
out << outputMdem(mdemBuffer->questions, mdemBuffer->trainedAt);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
mdemBuffer = new MdemBuffer;
|
mdemBuffer = new MdemBuffer;
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
@@ -952,9 +966,9 @@ int main(int argc, char *argv[]) {
|
|||||||
hlButtonsTop->addWidget(add);
|
hlButtonsTop->addWidget(add);
|
||||||
hlButtonsTop->addWidget(save);
|
hlButtonsTop->addWidget(save);
|
||||||
hlButtonsTop->addWidget(load);
|
hlButtonsTop->addWidget(load);
|
||||||
cbAlgorithm->addItem("Primary", PRIMARY);
|
|
||||||
cbAlgorithm->addItem("Random", RANDOM);
|
|
||||||
cbAlgorithm->addItem("Spaced", SPACED);
|
cbAlgorithm->addItem("Spaced", SPACED);
|
||||||
|
cbAlgorithm->addItem("Random", RANDOM);
|
||||||
|
cbAlgorithm->addItem("Primary", PRIMARY);
|
||||||
practice->setText("Practice");
|
practice->setText("Practice");
|
||||||
hlButtonsBottom->addWidget(cbAlgorithm);
|
hlButtonsBottom->addWidget(cbAlgorithm);
|
||||||
hlButtonsBottom->addWidget(practice);
|
hlButtonsBottom->addWidget(practice);
|
||||||
@@ -972,9 +986,7 @@ int main(int argc, char *argv[]) {
|
|||||||
});
|
});
|
||||||
QObject::connect(load, &QToolButton::clicked, &reloadMdem);
|
QObject::connect(load, &QToolButton::clicked, &reloadMdem);
|
||||||
QObject::connect(save, &QToolButton::clicked, []() {
|
QObject::connect(save, &QToolButton::clicked, []() {
|
||||||
auto filename = getFilename(currentMdem);
|
saveMdem();
|
||||||
std::ofstream out(currentMdem);
|
|
||||||
out << outputMdem(mdemBuffer->questions, mdemBuffer->trainedAt);
|
|
||||||
});
|
});
|
||||||
QObject::connect(
|
QObject::connect(
|
||||||
practice,
|
practice,
|
||||||
@@ -983,9 +995,8 @@ int main(int argc, char *argv[]) {
|
|||||||
trainWindow->show();
|
trainWindow->show();
|
||||||
trainWindow->resize(600, 300);
|
trainWindow->resize(600, 300);
|
||||||
initiatePractice(
|
initiatePractice(
|
||||||
mdemBuffer->questions,
|
mdemBuffer,
|
||||||
static_cast<PracticeAlgorithm>(cbAlgorithm->currentData().toInt()),
|
static_cast<PracticeAlgorithm>(cbAlgorithm->currentData().toInt())
|
||||||
&mdemBuffer->trainedAt
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,14 +2,14 @@
|
|||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QToolButton>
|
#include <QToolButton>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <atomic>
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <ctime>
|
||||||
#include <format>
|
#include <format>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <exception>
|
|
||||||
#include <format>
|
#include <format>
|
||||||
#include <qabstractitemview.h>
|
#include <qabstractitemview.h>
|
||||||
#include <qboxlayout.h>
|
#include <qboxlayout.h>
|
||||||
|
#include <qchar.h>
|
||||||
#include <qcoreevent.h>
|
#include <qcoreevent.h>
|
||||||
#include <qlabel.h>
|
#include <qlabel.h>
|
||||||
#include <qlayoutitem.h>
|
#include <qlayoutitem.h>
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
#include <qnamespace.h>
|
#include <qnamespace.h>
|
||||||
#include <qscrollarea.h>
|
#include <qscrollarea.h>
|
||||||
#include <qstandarditemmodel.h>
|
#include <qstandarditemmodel.h>
|
||||||
|
#include <qstringalgorithms.h>
|
||||||
#include <qstringlistmodel.h>
|
#include <qstringlistmodel.h>
|
||||||
#include <qtoolbutton.h>
|
#include <qtoolbutton.h>
|
||||||
#include <Qt>
|
#include <Qt>
|
||||||
@@ -29,6 +30,7 @@
|
|||||||
#include <QStyledItemDelegate>
|
#include <QStyledItemDelegate>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
#include "trainWindow.h"
|
#include "trainWindow.h"
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
|
|
||||||
@@ -204,7 +206,6 @@ QLabel *lQuestionText;
|
|||||||
QVBoxLayout *vButtonBox;
|
QVBoxLayout *vButtonBox;
|
||||||
QWidget *actionButtons;
|
QWidget *actionButtons;
|
||||||
QHBoxLayout *hButtons;
|
QHBoxLayout *hButtons;
|
||||||
QToolButton *btnPrev;
|
|
||||||
QSpacerItem *leftSpacer;
|
QSpacerItem *leftSpacer;
|
||||||
QToolButton *btnTriggerAnswer;
|
QToolButton *btnTriggerAnswer;
|
||||||
QSpacerItem *rightSpacer;
|
QSpacerItem *rightSpacer;
|
||||||
@@ -229,10 +230,12 @@ QVBoxLayout *vGroups;
|
|||||||
std::vector<QStandardItemModel*> groupModels;
|
std::vector<QStandardItemModel*> groupModels;
|
||||||
|
|
||||||
// Questions & State
|
// Questions & State
|
||||||
std::vector<Question*> trainQuestions = std::vector<Question*>();
|
MdemBuffer *currentBuffer;
|
||||||
|
|
||||||
int32_t currentQuestionIndex = -1;
|
int32_t currentQuestionIndex = -1;
|
||||||
std::vector<GroupView*> groupViews;
|
std::vector<GroupView*> groupViews;
|
||||||
PracticeAlgorithm practiceAlgoritm;
|
PracticeAlgorithm practiceAlgoritm;
|
||||||
|
time_t lastTrainedAt;
|
||||||
|
|
||||||
std::default_random_engine rng;
|
std::default_random_engine rng;
|
||||||
std::vector<QStandardItem*> itemPool;
|
std::vector<QStandardItem*> itemPool;
|
||||||
@@ -285,6 +288,11 @@ void hideQuestionElements() {
|
|||||||
orderList->hide();
|
orderList->hide();
|
||||||
multiChoiceList->hide();
|
multiChoiceList->hide();
|
||||||
wGroupQuestion->hide();
|
wGroupQuestion->hide();
|
||||||
|
btnNotRemembered->hide();
|
||||||
|
btnHard->hide();
|
||||||
|
btnMedium->hide();
|
||||||
|
btnEasy->hide();
|
||||||
|
btnTriggerAnswer->hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupAnswerQuestion(MultiElementQuestion *question) {
|
void setupAnswerQuestion(MultiElementQuestion *question) {
|
||||||
@@ -308,6 +316,10 @@ void setupAnswerQuestion(MultiElementQuestion *question) {
|
|||||||
[](bool checked) {
|
[](bool checked) {
|
||||||
answerText->show();
|
answerText->show();
|
||||||
btnTriggerAnswer->hide();
|
btnTriggerAnswer->hide();
|
||||||
|
btnNotRemembered->show();
|
||||||
|
btnHard->show();
|
||||||
|
btnMedium->show();
|
||||||
|
btnEasy->show();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
btnTriggerAnswer->show();
|
btnTriggerAnswer->show();
|
||||||
@@ -491,63 +503,132 @@ void setupQuestion(Question *question) {
|
|||||||
hideQuestionElements();
|
hideQuestionElements();
|
||||||
releaseAllItems();
|
releaseAllItems();
|
||||||
QObject::disconnect(btnTriggerAnswer, 0, 0, 0);
|
QObject::disconnect(btnTriggerAnswer, 0, 0, 0);
|
||||||
if (auto *question = dynamic_cast<MultiElementQuestion*>(trainQuestions[currentQuestionIndex])) {
|
if (auto *q = dynamic_cast<MultiElementQuestion*>(question)) {
|
||||||
btnNotRemembered->hide();
|
switch (q->type) {
|
||||||
btnHard->hide();
|
|
||||||
btnMedium->hide();
|
|
||||||
btnEasy->hide();
|
|
||||||
switch (question->type) {
|
|
||||||
case MultiElementType::Order:
|
case MultiElementType::Order:
|
||||||
setupOrderQuestion(question);
|
setupOrderQuestion(q);
|
||||||
break;
|
break;
|
||||||
case MultiElementType::MultiChoice:
|
case MultiElementType::MultiChoice:
|
||||||
setupMultiChoiceQuestion(question);
|
setupMultiChoiceQuestion(q);
|
||||||
break;
|
break;
|
||||||
case MultiElementType::Regular:
|
case MultiElementType::Regular:
|
||||||
setupAnswerQuestion(question);
|
setupAnswerQuestion(q);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (auto *question = dynamic_cast<GroupQuestion*>(trainQuestions[currentQuestionIndex])) {
|
} else if (auto *question = dynamic_cast<GroupQuestion*>(
|
||||||
|
currentBuffer->questions[currentQuestionIndex])
|
||||||
|
) {
|
||||||
setupGroupQuestion(question);
|
setupGroupQuestion(question);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void updatePaginationVisibility() {
|
void updatePaginationVisibility() {
|
||||||
if (currentQuestionIndex == 0) {
|
if (currentQuestionIndex == currentBuffer->questions.size() - 1) {
|
||||||
btnPrev->hide();
|
|
||||||
} else {
|
|
||||||
btnPrev->show();
|
|
||||||
}
|
|
||||||
if (currentQuestionIndex == trainQuestions.size() - 1) {
|
|
||||||
btnNext->hide();
|
btnNext->hide();
|
||||||
} else {
|
} else {
|
||||||
btnNext->show();
|
btnNext->show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void initiatePractice(
|
template <typename T>
|
||||||
std::vector<Question*> questions,
|
int randomIndex(std::vector<T> *vec) {
|
||||||
PracticeAlgorithm algorithm,
|
srand(time(NULL));
|
||||||
time_t *trainedAt
|
return rand() % vec->size();
|
||||||
) {
|
}
|
||||||
|
|
||||||
|
time_t getTime() {
|
||||||
auto now = std::chrono::system_clock::now();
|
auto now = std::chrono::system_clock::now();
|
||||||
time_t unix_timestamp = std::chrono::duration_cast<std::chrono::seconds>(
|
return std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
now.time_since_epoch()
|
now.time_since_epoch()
|
||||||
).count();
|
).count();
|
||||||
|
}
|
||||||
|
|
||||||
trainQuestions = questions;
|
void setupNextQuestion() {
|
||||||
if (questions.size() <= 0) {
|
if (currentBuffer->questions.size() <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
currentQuestionIndex = 0;
|
switch (practiceAlgoritm) {
|
||||||
|
case PRIMARY: {
|
||||||
|
currentQuestionIndex++;
|
||||||
|
if (currentQuestionIndex < currentBuffer->questions.size()) {
|
||||||
|
setupQuestion(currentBuffer->questions[currentQuestionIndex]);
|
||||||
|
}
|
||||||
updatePaginationVisibility();
|
updatePaginationVisibility();
|
||||||
setupQuestion(trainQuestions[currentQuestionIndex]);
|
} break;
|
||||||
practiceAlgoritm = algorithm;
|
case RANDOM: {
|
||||||
|
auto questionCandidates = currentBuffer->questions;
|
||||||
|
if (currentQuestionIndex > -1) {
|
||||||
|
questionCandidates.erase(questionCandidates.begin() + currentQuestionIndex);
|
||||||
|
}
|
||||||
|
if (questionCandidates.size() > 0) {
|
||||||
|
auto i = randomIndex(&questionCandidates);
|
||||||
|
setupQuestion(questionCandidates[i]);
|
||||||
|
for (int k = 0; k < currentBuffer->questions.size(); ++k) {
|
||||||
|
if (currentBuffer->questions[k] == questionCandidates[i]) {
|
||||||
|
currentQuestionIndex = k;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case SPACED: {
|
||||||
|
auto questionCandidates = std::vector<Question*>();
|
||||||
|
time_t time = getTime();
|
||||||
|
auto lastTrainedAt = currentBuffer->trainedAt;
|
||||||
|
currentBuffer->trainedAt = time;
|
||||||
|
for (int i = 0; i < currentBuffer->questions.size(); ++i) {
|
||||||
|
auto cooldownSeconds = currentBuffer->questions[i]->Cooldown * 3600;
|
||||||
|
auto cooldownEndsAt = lastTrainedAt + cooldownSeconds;
|
||||||
|
if (i != currentQuestionIndex && cooldownEndsAt <= time) {
|
||||||
|
questionCandidates.push_back(currentBuffer->questions[i]);
|
||||||
|
}
|
||||||
|
auto newCooldown = cooldownEndsAt - time;
|
||||||
|
if (newCooldown < 0) {
|
||||||
|
newCooldown = 0;
|
||||||
|
}
|
||||||
|
currentBuffer->questions[i]->Cooldown = (double)newCooldown / 3600;
|
||||||
|
}
|
||||||
|
if (questionCandidates.size() > 0) {
|
||||||
|
auto i = randomIndex(&questionCandidates);
|
||||||
|
setupQuestion(questionCandidates[i]);
|
||||||
|
setupQuestion(
|
||||||
|
questionCandidates[randomIndex(&questionCandidates)]
|
||||||
|
);
|
||||||
|
for (int k = 0; k < currentBuffer->questions.size(); ++k) {
|
||||||
|
if (currentBuffer->questions[k] == questionCandidates[i]) {
|
||||||
|
currentQuestionIndex = k;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
btnNotRemembered->hide();
|
void initiatePractice(
|
||||||
btnHard->hide();
|
MdemBuffer *mdemBuffer,
|
||||||
btnMedium->hide();
|
PracticeAlgorithm algorithm
|
||||||
btnEasy->hide();
|
) {
|
||||||
|
practiceAlgoritm = algorithm;
|
||||||
|
currentBuffer = mdemBuffer;
|
||||||
|
|
||||||
|
if (currentBuffer->questions.size() <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentQuestionIndex = -1;
|
||||||
|
hideQuestionElements();
|
||||||
|
updatePaginationVisibility();
|
||||||
|
setupNextQuestion();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCooldownHours(int cooldown) {
|
||||||
|
time_t time = getTime();
|
||||||
|
currentBuffer->trainedAt = time;
|
||||||
|
auto question = currentBuffer->questions[currentQuestionIndex];
|
||||||
|
question->Cooldown = cooldown;
|
||||||
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void initTrainWindow() {
|
void initTrainWindow() {
|
||||||
@@ -566,6 +647,25 @@ void initTrainWindow() {
|
|||||||
|
|
||||||
itemDelegate = new CustomItemDelegate(multiChoiceList);
|
itemDelegate = new CustomItemDelegate(multiChoiceList);
|
||||||
|
|
||||||
|
{ // Top button menu.
|
||||||
|
auto hTopButtons = new QHBoxLayout();
|
||||||
|
auto topButtons = new QWidget();
|
||||||
|
topButtons->setLayout(hTopButtons);
|
||||||
|
|
||||||
|
auto topLeftSpacer = new QSpacerItem(50, 50, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||||
|
auto btnSaveProgress = new QToolButton();
|
||||||
|
btnSaveProgress->setText("Save progress");
|
||||||
|
|
||||||
|
hTopButtons->addItem(topLeftSpacer);
|
||||||
|
hTopButtons->addWidget(btnSaveProgress);
|
||||||
|
vTrainWidget->addWidget(topButtons);
|
||||||
|
|
||||||
|
QObject::connect(btnSaveProgress, &QToolButton::clicked, []() {
|
||||||
|
saveMdem();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
{ // Make the question box.
|
{ // Make the question box.
|
||||||
questionBox = new QWidget();
|
questionBox = new QWidget();
|
||||||
vQuestionBox = new QVBoxLayout();
|
vQuestionBox = new QVBoxLayout();
|
||||||
@@ -639,16 +739,26 @@ void initTrainWindow() {
|
|||||||
vButtonBox = new QVBoxLayout();
|
vButtonBox = new QVBoxLayout();
|
||||||
actionButtons = new QWidget();
|
actionButtons = new QWidget();
|
||||||
hButtons = new QHBoxLayout();
|
hButtons = new QHBoxLayout();
|
||||||
btnPrev = new QToolButton();
|
|
||||||
leftSpacer = new QSpacerItem(50, 50, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
leftSpacer = new QSpacerItem(50, 50, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||||
|
|
||||||
btnTriggerAnswer = new QToolButton();
|
btnTriggerAnswer = new QToolButton();
|
||||||
|
|
||||||
btnNotRemembered = new QToolButton();
|
btnNotRemembered = new QToolButton();
|
||||||
|
QObject::connect(btnNotRemembered, &QToolButton::clicked, []() {
|
||||||
|
setCooldownHours(0);
|
||||||
|
});
|
||||||
btnHard = new QToolButton();
|
btnHard = new QToolButton();
|
||||||
|
QObject::connect(btnHard, &QToolButton::clicked, []() {
|
||||||
|
setCooldownHours(12);
|
||||||
|
});
|
||||||
btnMedium = new QToolButton();
|
btnMedium = new QToolButton();
|
||||||
|
QObject::connect(btnMedium, &QToolButton::clicked, []() {
|
||||||
|
setCooldownHours(48);
|
||||||
|
});
|
||||||
btnEasy = new QToolButton();
|
btnEasy = new QToolButton();
|
||||||
|
QObject::connect(btnEasy, &QToolButton::clicked, []() {
|
||||||
|
setCooldownHours(72);
|
||||||
|
});
|
||||||
btnNotRemembered->setText("Not remembered");
|
btnNotRemembered->setText("Not remembered");
|
||||||
btnHard->setText("Hard");
|
btnHard->setText("Hard");
|
||||||
btnMedium->setText("Medium");
|
btnMedium->setText("Medium");
|
||||||
@@ -659,17 +769,10 @@ void initTrainWindow() {
|
|||||||
rightSpacer = new QSpacerItem(50, 50, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
rightSpacer = new QSpacerItem(50, 50, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||||
btnNext = new QToolButton();
|
btnNext = new QToolButton();
|
||||||
|
|
||||||
hButtons->addWidget(btnPrev);
|
|
||||||
hButtons->addItem(leftSpacer);
|
hButtons->addItem(leftSpacer);
|
||||||
hButtons->addWidget(btnTriggerAnswer);
|
hButtons->addWidget(btnTriggerAnswer);
|
||||||
|
|
||||||
hButtons->addWidget(btnNotRemembered);
|
hButtons->addWidget(btnNotRemembered);
|
||||||
hButtons->addWidget(btnHard);
|
hButtons->addWidget(btnHard);
|
||||||
QObject::connect(btnHard, &QToolButton::clicked, []() {
|
|
||||||
const double hourCooldown = 25.0;
|
|
||||||
auto question = trainQuestions[currentQuestionIndex];
|
|
||||||
question->Cooldown = hourCooldown;
|
|
||||||
});
|
|
||||||
hButtons->addWidget(btnMedium);
|
hButtons->addWidget(btnMedium);
|
||||||
hButtons->addWidget(btnEasy);
|
hButtons->addWidget(btnEasy);
|
||||||
|
|
||||||
@@ -677,34 +780,13 @@ void initTrainWindow() {
|
|||||||
hButtons->addWidget(btnNext);
|
hButtons->addWidget(btnNext);
|
||||||
vButtonBox->addWidget(actionButtons);
|
vButtonBox->addWidget(actionButtons);
|
||||||
actionButtons->setLayout(hButtons);
|
actionButtons->setLayout(hButtons);
|
||||||
btnPrev->setText("Previous");
|
|
||||||
QObject::connect(btnPrev, &QToolButton::clicked, []() {
|
|
||||||
currentQuestionIndex--;
|
|
||||||
if (currentQuestionIndex > -1) {
|
|
||||||
setupQuestion(trainQuestions[currentQuestionIndex]);
|
|
||||||
}
|
|
||||||
updatePaginationVisibility();
|
|
||||||
});
|
|
||||||
|
|
||||||
btnTriggerAnswer->setText("Show answer");
|
btnTriggerAnswer->setText("Show answer");
|
||||||
|
btnTriggerAnswer->hide();
|
||||||
|
|
||||||
btnNext->setText("Next");
|
btnNext->setText("Next");
|
||||||
QObject::connect(btnNext, &QToolButton::clicked, []() {
|
QObject::connect(btnNext, &QToolButton::clicked, []() {
|
||||||
switch (practiceAlgoritm) {
|
setupNextQuestion();
|
||||||
case PRIMARY:
|
|
||||||
currentQuestionIndex++;
|
|
||||||
if (currentQuestionIndex < trainQuestions.size()) {
|
|
||||||
setupQuestion(trainQuestions[currentQuestionIndex]);
|
|
||||||
}
|
|
||||||
updatePaginationVisibility();
|
|
||||||
break;
|
|
||||||
case RANDOM:
|
|
||||||
// TODO: implement
|
|
||||||
break;
|
|
||||||
case SPACED:
|
|
||||||
// TODO: implement
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
questionBox->setObjectName("question-box");
|
questionBox->setObjectName("question-box");
|
||||||
actionButtons->setStyleSheet(QString(
|
actionButtons->setStyleSheet(QString(
|
||||||
|
|||||||
@@ -141,6 +141,18 @@ Result<NoneType> ValidateGrammar(const std::vector<Token>& tokens) {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
time_t parseToUTCTime(const std::string datetime, std::string format) {
|
||||||
|
std::tm tm = {};
|
||||||
|
std::istringstream ss(datetime);
|
||||||
|
ss >> std::get_time(&tm, format.c_str());
|
||||||
|
if (ss.fail()) {
|
||||||
|
throw std::runtime_error("Failed to parse datetime string");
|
||||||
|
}
|
||||||
|
std::time_t time = timegm(&tm);
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Result<ParseInfo> parseQuestions(const std::vector<Token>& tokens) {
|
Result<ParseInfo> parseQuestions(const std::vector<Token>& tokens) {
|
||||||
auto questions = std::vector<Question*>();
|
auto questions = std::vector<Question*>();
|
||||||
time_t time = 0;
|
time_t time = 0;
|
||||||
@@ -178,16 +190,14 @@ Result<ParseInfo> parseQuestions(const std::vector<Token>& tokens) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (isInBounds(i) && tokens[i].tokenType == TokenType::TextFragment) {
|
if (isInBounds(i) && tokens[i].tokenType == TokenType::TextFragment) {
|
||||||
std::tm tm = {};
|
|
||||||
try {
|
try {
|
||||||
strptime(tokens[i].content.c_str(), "%d.%m.%Y %H:%M", &tm);
|
time = parseToUTCTime(tokens[i].content.c_str(), "%d.%m.%Y %H:%M");
|
||||||
} catch (std::exception e) {
|
} catch (std::exception e) {
|
||||||
return makeResult(
|
return makeResult(
|
||||||
std::format("cannot parse the time - {}", e.what()),
|
std::format("cannot parse the time - {}", e.what()),
|
||||||
tokens[i]
|
tokens[i]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
time = mktime(&tm);
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user