From d24f813f3f2a05c112e803e4256b53535895fc98 Mon Sep 17 00:00:00 2001 From: Jedidiah Barber Date: Wed, 14 Jul 2021 11:49:10 +1200 Subject: Initial mirror commit --- src/statistics/BaseStatPage.cpp | 38 ++++++++ src/statistics/BaseStatPage.h | 31 ++++++ src/statistics/ProgressPage.cpp | 41 ++++++++ src/statistics/ProgressPage.h | 33 +++++++ src/statistics/ScheduledPage.cpp | 35 +++++++ src/statistics/ScheduledPage.h | 22 +++++ src/statistics/StatisticsParams.cpp | 2 + src/statistics/StatisticsParams.h | 20 ++++ src/statistics/StatisticsView.cpp | 182 ++++++++++++++++++++++++++++++++++++ src/statistics/StatisticsView.h | 53 +++++++++++ src/statistics/StudiedPage.cpp | 17 ++++ src/statistics/StudiedPage.h | 18 ++++ src/statistics/TimeChartPage.cpp | 54 +++++++++++ src/statistics/TimeChartPage.h | 33 +++++++ 14 files changed, 579 insertions(+) create mode 100644 src/statistics/BaseStatPage.cpp create mode 100644 src/statistics/BaseStatPage.h create mode 100644 src/statistics/ProgressPage.cpp create mode 100644 src/statistics/ProgressPage.h create mode 100644 src/statistics/ScheduledPage.cpp create mode 100644 src/statistics/ScheduledPage.h create mode 100644 src/statistics/StatisticsParams.cpp create mode 100644 src/statistics/StatisticsParams.h create mode 100644 src/statistics/StatisticsView.cpp create mode 100644 src/statistics/StatisticsView.h create mode 100644 src/statistics/StudiedPage.cpp create mode 100644 src/statistics/StudiedPage.h create mode 100644 src/statistics/TimeChartPage.cpp create mode 100644 src/statistics/TimeChartPage.h (limited to 'src/statistics') diff --git a/src/statistics/BaseStatPage.cpp b/src/statistics/BaseStatPage.cpp new file mode 100644 index 0000000..a7a8764 --- /dev/null +++ b/src/statistics/BaseStatPage.cpp @@ -0,0 +1,38 @@ +#include "BaseStatPage.h" +#include "StatisticsParams.h" + +BaseStatPage::BaseStatPage(const StatisticsParams* statParams): + statParams(statParams) +{ +} + +void BaseStatPage::init() +{ + createUi(); + updateDataSet(); +} + +void BaseStatPage::createUi() +{ + QVBoxLayout* mainLt = new QVBoxLayout; + mainLt->addWidget(createTitleLabel()); + mainLt->addWidget(createChart()); + mainLt->addWidget(createTotalReviewsLabel()); + setLayout(mainLt); +} + +QLabel* BaseStatPage::createTitleLabel() +{ + QLabel* titleLabel = new QLabel(getTitle()); + titleLabel->setFont(QFont("Sans Serif", 18, QFont::Bold)); + titleLabel->setAlignment(Qt::AlignCenter); + return titleLabel; +} + +QWidget* BaseStatPage::createTotalReviewsLabel() +{ + totalReviewsLabel = new QLabel; + totalReviewsLabel->setFont(QFont("Sans Serif", 16)); + totalReviewsLabel->setAlignment(Qt::AlignRight); + return totalReviewsLabel; +} diff --git a/src/statistics/BaseStatPage.h b/src/statistics/BaseStatPage.h new file mode 100644 index 0000000..7ef91bd --- /dev/null +++ b/src/statistics/BaseStatPage.h @@ -0,0 +1,31 @@ +#ifndef STATISTICS_PAGE_H +#define STATISTICS_PAGE_H + +#include + +class StatisticsParams; + +class BaseStatPage: public QWidget +{ + Q_OBJECT +public: + BaseStatPage(const StatisticsParams* statParams); + virtual void updateDataSet() = 0; + virtual QString getTitle() const = 0; + virtual bool usesTimePeriod() const = 0; + +protected: + void init(); + virtual QWidget* createChart() = 0; + +private: + void createUi(); + QLabel* createTitleLabel(); + QWidget* createTotalReviewsLabel(); + +protected: + const StatisticsParams* statParams; + QLabel* totalReviewsLabel; +}; + +#endif diff --git a/src/statistics/ProgressPage.cpp b/src/statistics/ProgressPage.cpp new file mode 100644 index 0000000..7676173 --- /dev/null +++ b/src/statistics/ProgressPage.cpp @@ -0,0 +1,41 @@ +#include "ProgressPage.h" +#include "StatisticsParams.h" +#include "../charts/PieChart.h" +#include "../dictionary/CardPack.h" + +ProgressPage::ProgressPage(const StatisticsParams* statParams): + BaseStatPage(statParams) +{ + init(); +} + +QWidget* ProgressPage::createChart() +{ + chart = new PieChart; + chart->setColors({"#39c900", "#ece900", "#ff0000"}); + return chart; +} + +void ProgressPage::updateDataSet() +{ + updateCardsNumbers(); + totalReviewsLabel->setText(tr("Total: %1").arg(allCardsNum)); + chart->setDataSet(getDataSet()); +} + +void ProgressPage::updateCardsNumbers() +{ + const CardPack* pack = statParams->getCardPack(); + allCardsNum = pack->cardsNum(); + newCardsNum = pack->getNewCards().size(); + toBeRepeated = pack->getActiveCards().size(); +} + +QList ProgressPage::getDataSet() const +{ + QList dataSet; + dataSet << DataPoint(tr("Studied"), allCardsNum - newCardsNum - toBeRepeated); + dataSet << DataPoint(tr("Scheduled for today"), toBeRepeated); + dataSet << DataPoint(tr("New"), newCardsNum); + return dataSet; +} diff --git a/src/statistics/ProgressPage.h b/src/statistics/ProgressPage.h new file mode 100644 index 0000000..9adb4cc --- /dev/null +++ b/src/statistics/ProgressPage.h @@ -0,0 +1,33 @@ +#ifndef PROGRESS_PAGE_H +#define PROGRESS_PAGE_H + +#include +#include "BaseStatPage.h" +#include "../charts/DataPoint.h" + +class PieChart; + +class ProgressPage: public BaseStatPage +{ + Q_OBJECT +public: + ProgressPage(const StatisticsParams* statParams); + void updateDataSet(); + QString getTitle() const { return tr("Study progress"); } + bool usesTimePeriod() const {return false;} + +protected: + QWidget* createChart(); + +private: + void updateCardsNumbers(); + QList getDataSet() const; + +private: + PieChart* chart; + int allCardsNum; + int newCardsNum; + int toBeRepeated; +}; + +#endif diff --git a/src/statistics/ScheduledPage.cpp b/src/statistics/ScheduledPage.cpp new file mode 100644 index 0000000..5e2b558 --- /dev/null +++ b/src/statistics/ScheduledPage.cpp @@ -0,0 +1,35 @@ +#include "ScheduledPage.h" +#include "../dictionary/CardPack.h" + +ScheduledPage::ScheduledPage(const StatisticsParams* statParams): + TimeChartPage(statParams) +{ + init(); +} + +QList ScheduledPage::getDates(const CardPack* pack) const +{ + QList scheduled = pack->getScheduledDates(); + adjustScheduledRecords(scheduled); + return scheduled; +} + +void ScheduledPage::adjustScheduledRecords(QList& scheduled) +{ + const QDate curDate = QDate::currentDate(); + const QTime zeroTime = QTime(0, 0); + for(int i = 0; i < scheduled.size(); i++) + { + QDateTime& dateTime = scheduled[i]; + if(dateTime.date() < curDate) + dateTime = QDateTime(curDate, zeroTime); + else if(laterThisDay(dateTime)) + dateTime = QDateTime(curDate.addDays(1), zeroTime); + } +} + +bool ScheduledPage::laterThisDay(QDateTime& dateTime) +{ + return dateTime.date() == QDate::currentDate() && + dateTime > QDateTime::currentDateTime(); +} diff --git a/src/statistics/ScheduledPage.h b/src/statistics/ScheduledPage.h new file mode 100644 index 0000000..f5a4eec --- /dev/null +++ b/src/statistics/ScheduledPage.h @@ -0,0 +1,22 @@ +#ifndef SCHEDULED_PAGE_H +#define SCHEDULED_PAGE_H + +#include "TimeChartPage.h" + +class ScheduledPage: public TimeChartPage +{ + Q_OBJECT +public: + ScheduledPage(const StatisticsParams* statParams); + QString getTitle() const { return tr("Scheduled cards"); } + +protected: + QList getDates(const CardPack* pack) const; + int getDataDirection() const { return 1; } + +private: + static void adjustScheduledRecords(QList& scheduled); + static bool laterThisDay(QDateTime& dateTime); +}; + +#endif diff --git a/src/statistics/StatisticsParams.cpp b/src/statistics/StatisticsParams.cpp new file mode 100644 index 0000000..4e5d80f --- /dev/null +++ b/src/statistics/StatisticsParams.cpp @@ -0,0 +1,2 @@ +#include "StatisticsParams.h" +#include "../dictionary/CardPack.h" diff --git a/src/statistics/StatisticsParams.h b/src/statistics/StatisticsParams.h new file mode 100644 index 0000000..9a805c6 --- /dev/null +++ b/src/statistics/StatisticsParams.h @@ -0,0 +1,20 @@ +#ifndef STATISTICS_PARAMS_H +#define STATISTICS_PARAMS_H + +class CardPack; + +class StatisticsParams +{ +public: + static const int Week = 7; + +public: + const CardPack* getCardPack() const { return cardPack; } + int getTimePeriod() const { return timePeriod; } + +protected: + const CardPack* cardPack; + int timePeriod; +}; + +#endif diff --git a/src/statistics/StatisticsView.cpp b/src/statistics/StatisticsView.cpp new file mode 100644 index 0000000..de1b16d --- /dev/null +++ b/src/statistics/StatisticsView.cpp @@ -0,0 +1,182 @@ +#include "StatisticsView.h" +#include "StudiedPage.h" +#include "ScheduledPage.h" +#include "ProgressPage.h" +#include "../dictionary/Dictionary.h" +#include "../dictionary/CardPack.h" + +const QSize StatisticsView::GridSize(150, 110); + +StatisticsView::StatisticsView(const Dictionary* dict): + dict(dict), periodLabel(NULL), periodBox(NULL) +{ + init(); + createContentsList(); + createPages(); + createListItems(); + createUi(); + loadSettings(); +} + +void StatisticsView::init() +{ + cardPack = dict->cardPacks().first(); + timePeriod = Week; + setWindowTitle(tr("Statistics") + " - " + dict->shortName()); + setWindowIcon(QIcon(":/images/statistics.png")); +} + +void StatisticsView::closeEvent(QCloseEvent* /*event*/) +{ + saveSettings(); +} + +void StatisticsView::createPages() +{ + pagesWidget = new QStackedWidget; + pagesWidget->addWidget(new ProgressPage(this)); + pagesWidget->addWidget(new StudiedPage(this)); + pagesWidget->addWidget(new ScheduledPage(this)); +} + +void StatisticsView::createContentsList() +{ + contentsWidget = new QListWidget; + contentsWidget->setViewMode(QListView::IconMode); + contentsWidget->setGridSize(GridSize); + contentsWidget->setIconSize(QSize(IconSize, IconSize)); + contentsWidget->setMovement(QListView::Static); + contentsWidget->setFixedWidth(GridSize.width() + 4); +} + +void StatisticsView::createListItems() +{ + QStringList icons = {"pie-chart-3d", "chart-past", "chart-future"}; + for(int i = 0; i < icons.size(); i++) + new QListWidgetItem( QIcon(QString(":/images/%1.png").arg(icons[i])), + static_cast(pagesWidget->widget(i))->getTitle(), + contentsWidget); + connect(contentsWidget, + SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), + SLOT(changePage(QListWidgetItem*, QListWidgetItem*))); + contentsWidget->setCurrentRow(0); +} + +void StatisticsView::changePage(QListWidgetItem* curPage, QListWidgetItem* prevPage) +{ + if(!curPage) + curPage = prevPage; + pagesWidget->setCurrentIndex(contentsWidget->row(curPage)); + updateChart(); + updatePeriodBox(); +} + +void StatisticsView::createUi() +{ + QVBoxLayout* verLt = new QVBoxLayout; + verLt->addLayout(createControlLayout()); + verLt->addWidget(pagesWidget); + + QHBoxLayout* horLt = new QHBoxLayout; + horLt->addWidget(contentsWidget); + horLt->addLayout(verLt); + setLayout(horLt); +} + +void StatisticsView::loadSettings() +{ + QSettings settings; + QVariant pointVar = settings.value("stats-pos"); + if(pointVar.isNull()) + return; + move(pointVar.toPoint()); + resize(settings.value("stats-size").toSize()); +} + +void StatisticsView::saveSettings() +{ + QSettings settings; + settings.setValue("stats-pos", pos()); + settings.setValue("stats-size", size()); +} + +QBoxLayout* StatisticsView::createControlLayout() +{ + QHBoxLayout* lt = new QHBoxLayout; + lt->addWidget(new QLabel(tr("Card pack:"))); + lt->addWidget(createPacksBox()); + lt->addStretch(); + periodLabel = new QLabel(tr("Period:")); + lt->addWidget(periodLabel); + lt->addWidget(createPeriodBox()); + return lt; +} + +QComboBox* StatisticsView::createPacksBox() +{ + QComboBox* packsBox = new QComboBox; + foreach(CardPack* pack, dict->cardPacks()) + packsBox->addItem(pack->id()); + connect(packsBox, SIGNAL(activated(int)), SLOT(setPack(int))); + return packsBox; +} + +QComboBox* StatisticsView::createPeriodBox() +{ + periodBox = new QComboBox; + QPair period; + foreach(period, getPeriodsList()) + periodBox->addItem(period.first, period.second); + periodBox->setMaxVisibleItems(12); + connect(periodBox, SIGNAL(activated(int)), SLOT(setPeriod(int))); + updatePeriodBox(); + return periodBox; +} + +QList> StatisticsView::getPeriodsList() +{ + static const int Week = 7; + static const int Month = 30; + static const int Year = 365; + return { + qMakePair(tr("%n week(s)", 0, 1), Week), + qMakePair(tr("%n week(s)", 0, 2), 2 * Week), + qMakePair(tr("%n week(s)", 0, 4), 4 * Week), + qMakePair(tr("%n month(s)", 0, 1), Month + 1), + qMakePair(tr("%n month(s)", 0, 2), 2 * Month + 1), + qMakePair(tr("%n month(s)", 0, 3), 3 * Month + 2), + qMakePair(tr("%n month(s)", 0, 6), 6 * Month + 3), + qMakePair(tr("%n year(s)", 0, 1), Year), + qMakePair(tr("%n year(s)", 0, 2), 2 * Year), + qMakePair(tr("%n year(s)", 0, 3), 3 * Year), + qMakePair(tr("All time"), -1)}; +} + +void StatisticsView::setPack(int packIndex) +{ + cardPack = dict->cardPacks().at(packIndex); + updateChart(); +} + +void StatisticsView::setPeriod(int index) +{ + timePeriod = periodBox->itemData(index).toInt(); + updateChart(); +} + +void StatisticsView::updateChart() +{ + static_cast(pagesWidget->currentWidget())->updateDataSet(); +} + +void StatisticsView::updatePeriodBox() +{ + bool visiblePeriod = static_cast( + pagesWidget->currentWidget())->usesTimePeriod(); + if(periodBox) + { + periodLabel->setVisible(visiblePeriod); + periodBox->setVisible(visiblePeriod); + } +} + diff --git a/src/statistics/StatisticsView.h b/src/statistics/StatisticsView.h new file mode 100644 index 0000000..560f8b8 --- /dev/null +++ b/src/statistics/StatisticsView.h @@ -0,0 +1,53 @@ +#ifndef STATISTICS_VIEW_H +#define STATISTICS_VIEW_H + +#include +#include + +#include "StatisticsParams.h" + +class Dictionary; +class CardPack; + +class StatisticsView: public QDialog, public StatisticsParams +{ + Q_OBJECT +public: + StatisticsView(const Dictionary* dict); + +private: + static QList> getPeriodsList(); + +private: + void init(); + void closeEvent(QCloseEvent *event); + void createPages(); + void createContentsList(); + void createListItems(); + void createUi(); + void loadSettings(); + void saveSettings(); + QBoxLayout* createControlLayout(); + QComboBox* createPacksBox(); + QComboBox* createPeriodBox(); + void updateChart(); + void updatePeriodBox(); + +private slots: + void changePage(QListWidgetItem* curPage, QListWidgetItem* prevPage); + void setPack(int packIndex); + void setPeriod(int index); + +private: + static const QSize GridSize; + static const int IconSize = 75; + +private: + const Dictionary* dict; + QListWidget* contentsWidget; + QLabel* periodLabel; + QComboBox* periodBox; + QStackedWidget* pagesWidget; +}; + +#endif diff --git a/src/statistics/StudiedPage.cpp b/src/statistics/StudiedPage.cpp new file mode 100644 index 0000000..5dbe83b --- /dev/null +++ b/src/statistics/StudiedPage.cpp @@ -0,0 +1,17 @@ +#include "StudiedPage.h" +#include "../dictionary/CardPack.h" +#include "../study/StudyRecord.h" + +StudiedPage::StudiedPage(const StatisticsParams* statParams): + TimeChartPage(statParams) +{ + init(); +} + +QList StudiedPage::getDates(const CardPack* pack) const +{ + QList res; + foreach(StudyRecord record, pack->getStudyRecords()) + res << record.date; + return res; +} diff --git a/src/statistics/StudiedPage.h b/src/statistics/StudiedPage.h new file mode 100644 index 0000000..5ccb59b --- /dev/null +++ b/src/statistics/StudiedPage.h @@ -0,0 +1,18 @@ +#ifndef STUDIED_PAGE_H +#define STUDIED_PAGE_H + +#include "TimeChartPage.h" + +class StudiedPage: public TimeChartPage +{ + Q_OBJECT +public: + StudiedPage(const StatisticsParams* statParams); + QString getTitle() const { return tr("Studied cards"); } + +protected: + QList getDates(const CardPack* pack) const; + int getDataDirection() const { return -1; } +}; + +#endif diff --git a/src/statistics/TimeChartPage.cpp b/src/statistics/TimeChartPage.cpp new file mode 100644 index 0000000..62ca63b --- /dev/null +++ b/src/statistics/TimeChartPage.cpp @@ -0,0 +1,54 @@ +#include "TimeChartPage.h" +#include "StatisticsParams.h" +#include "../charts/TimeChart.h" +#include "../dictionary/CardPack.h" + +TimeChartPage::TimeChartPage(const StatisticsParams* statParams): + BaseStatPage(statParams) +{ +} + +QWidget* TimeChartPage::createChart() +{ + chart = new TimeChart; + chart->setLabels(tr("Date"), tr("Cards")); + return chart; +} + +void TimeChartPage::updateDataSet() +{ + QList dates = getDates(statParams->getCardPack()); + int period = statParams->getTimePeriod(); + if(period == -1) + period = getStudyPeriodLength(dates); + int reviewsNum = getReviewsNum(dates, period); + totalReviewsLabel->setText(tr("Total: %1").arg(reviewsNum)); + chart->setDates(dates, period, getDataDirection()); +} + +int TimeChartPage::getStudyPeriodLength(const QList& dates) const +{ + const int minPeriod = 7; + QDateTime minDate = QDateTime::currentDateTime(); + QDateTime maxDate = minDate; + foreach(QDateTime date, dates) + if(date < minDate) + minDate = date; + else if(date > maxDate) + maxDate = date; + int res = minDate.daysTo(maxDate) + 1; + if(res < minPeriod) + res = minPeriod; + return res; +} + +int TimeChartPage::getReviewsNum(const QList& dates, + int period) const +{ + QDateTime curDate = QDateTime::currentDateTime(); + int res = 0; + foreach(QDateTime date, dates) + if(qAbs(date.daysTo(curDate)) < period) + res++; + return res; +} diff --git a/src/statistics/TimeChartPage.h b/src/statistics/TimeChartPage.h new file mode 100644 index 0000000..5e48d4c --- /dev/null +++ b/src/statistics/TimeChartPage.h @@ -0,0 +1,33 @@ +#ifndef TIME_CHART_PAGE_H +#define TIME_CHART_PAGE_H + +#include +#include "BaseStatPage.h" +#include "../charts/DataPoint.h" + +class TimeChart; +class StatisticsParams; +class CardPack; + +class TimeChartPage: public BaseStatPage +{ + Q_OBJECT +public: + TimeChartPage(const StatisticsParams* statParams); + void updateDataSet(); + bool usesTimePeriod() const {return true;} + +protected: + virtual QList getDates(const CardPack* pack) const = 0; + virtual int getDataDirection() const = 0; + QWidget* createChart(); + +private: + int getStudyPeriodLength(const QList& dates) const; + int getReviewsNum(const QList& dates, int period) const; + +private: + TimeChart* chart; +}; + +#endif -- cgit