Skip to content

Commit

Permalink
Finish time entry store work
Browse files Browse the repository at this point in the history
  • Loading branch information
LorenDB committed Dec 7, 2022
1 parent eee2dad commit 292e293
Show file tree
Hide file tree
Showing 7 changed files with 283 additions and 207 deletions.
21 changes: 11 additions & 10 deletions src/DailyOverviewDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,11 @@ class TimeTableItem : public QTableWidgetItem

DailyOverviewDialog::DailyOverviewDialog(QSharedPointer<AbstractTimeServiceManager> manager,
QSharedPointer<User> user,
QSharedPointer<TimeEntryStore> entryStore,
QWidget *parent)
: QDialog{parent},
m_manager{manager},
m_user{user},
m_entries{entryStore},
m_totalTime{new QLabel},
m_chronologicalTable{new QTableWidget{0, 3, this}},
m_byProjectTable{new QTableWidget{0, 2, this}},
Expand All @@ -59,8 +60,7 @@ DailyOverviewDialog::DailyOverviewDialog(QSharedPointer<AbstractTimeServiceManag
m_byProjectTable->setSelectionMode(QTableWidget::NoSelection);

auto updateData = [this] {
auto todaysTime = m_user->getTimeEntries(std::nullopt, std::nullopt, QDateTime{m_day, QTime{}}, m_day.endOfDay());
std::reverse(todaysTime.begin(), todaysTime.end());
auto todaysTime = m_entries->constSliceByDate(QDateTime{m_day, QTime{}}, m_day.endOfDay());

m_totalTime->setText(
tr("Total time: ")
Expand All @@ -76,17 +76,18 @@ DailyOverviewDialog::DailyOverviewDialog(QSharedPointer<AbstractTimeServiceManag
.toString(QStringLiteral("h:mm:ss"))));

m_chronologicalTable->clearContents();
m_chronologicalTable->setRowCount(static_cast<int>(todaysTime.count()));
for (int i = 0; i < todaysTime.size(); ++i)
m_chronologicalTable->setRowCount(TimeLight::rangeSize(todaysTime));
for (auto [i, it] = std::tuple(m_chronologicalTable->rowCount() - 1, todaysTime.begin()); it != todaysTime.end();
--i, ++it)
{
m_chronologicalTable->setItem(i, 0, new TimeTableItem{todaysTime[i].start()});
m_chronologicalTable->setItem(i, 1, new TimeTableItem{todaysTime[i].end()});
m_chronologicalTable->setItem(i, 2, new QTableWidgetItem{todaysTime[i].project().name()});
m_chronologicalTable->setItem(i, 0, new TimeTableItem{it->start()});
m_chronologicalTable->setItem(i, 1, new TimeTableItem{it->end()});
m_chronologicalTable->setItem(i, 2, new QTableWidgetItem{it->project().name()});
}

QHash<QString, int> msecsByProject;
for (int i = 0; i < todaysTime.size(); ++i)
msecsByProject[todaysTime[i].project().id()] += todaysTime[i].start().msecsTo(todaysTime[i].end());
for (const auto &t : todaysTime)
msecsByProject[t.project().id()] += t.start().msecsTo(t.end());
m_byProjectTable->clearContents();
m_byProjectTable->setRowCount(static_cast<int>(msecsByProject.count()));
int _i = 0;
Expand Down
4 changes: 3 additions & 1 deletion src/DailyOverviewDialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <QVBoxLayout>

#include "AbstractTimeServiceManager.h"
#include "TimeEntryStore.h"
#include "User.h"

class DailyOverviewDialog : public QDialog
Expand All @@ -16,6 +17,7 @@ class DailyOverviewDialog : public QDialog
public:
DailyOverviewDialog(QSharedPointer<AbstractTimeServiceManager> manager,
QSharedPointer<User> user,
QSharedPointer<TimeEntryStore> entryStore,
QWidget *parent = nullptr);

private:
Expand All @@ -26,7 +28,7 @@ class DailyOverviewDialog : public QDialog
};

QSharedPointer<AbstractTimeServiceManager> m_manager;
QSharedPointer<User> m_user;
QSharedPointer<TimeEntryStore> m_entries;

QLabel *m_totalTime;
QTableWidget *m_chronologicalTable;
Expand Down
193 changes: 90 additions & 103 deletions src/TimeEntryStore.cpp
Original file line number Diff line number Diff line change
@@ -1,147 +1,134 @@
#include "TimeEntryStore.h"

#include <QMutexLocker>
#include <QThread>
#include <QTableView>

#include "Logger.h"

TimeEntryStore::TimeEntryStore(QSharedPointer<User> user, QObject *parent)
: QObject{parent},
: QAbstractListModel{parent},
m_user{user},
m_nextPage{m_user->manager()->paginationStartsAt()}
{
fetchMore();

connect(&m_expiryTimer, &QTimer::timeout, this, &TimeEntryStore::clearStore);
m_expiryTimer.setInterval(3 * 60 * 60 * 1000);
m_expiryTimer.setInterval(2 * 60 * 60 * 1000);
m_expiryTimer.start();
}

QVariant TimeEntryStore::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() < 0 || index.row() >= m_store.size())
return {};

switch (role)
{
case Roles::Id:
return m_store[index.row()].id();
case Roles::UserId:
return m_store[index.row()].userId();
case Roles::Start:
return m_store[index.row()].start();
case Roles::End:
return m_store[index.row()].end();
case Roles::Project:
// return m_store[index.row()].project();
return m_store[index.row()].project().name();
case Roles::IsRunning:
return m_store[index.row()].running().value_or(false);
default:
return {};
}
}

QHash<int, QByteArray> TimeEntryStore::roleNames() const
{
return {
{Roles::Id, QByteArrayLiteral("id")},
{Roles::UserId, QByteArrayLiteral("userId")},
{Roles::Start, QByteArrayLiteral("start")},
{Roles::End, QByteArrayLiteral("end")},
{Roles::Project, QByteArrayLiteral("project")},
{Roles::IsRunning, QByteArrayLiteral("isRunning")},
};
}

void TimeEntryStore::fetchMore()
{
QMutexLocker _{&m_mut};
if (m_isAtEnd)
return;

TimeLight::logs::app()->trace("TimeEntryStore::fetchMore() on thread {}: lock mutex", QThread::currentThreadId());
QMutexLocker _{&m_mutex};
auto moreEntries = m_user->getTimeEntries(m_nextPage++);
if (moreEntries.isEmpty())
m_isAtEnd = true;
else
{
beginInsertRows({}, m_store.size(), m_store.size() + moreEntries.size() - 1);
m_store.append(moreEntries);
endInsertRows();
}
TimeLight::logs::app()->trace("TimeEntryStore::fetchMore() on thread {}: unlock mutex", QThread::currentThreadId());
}

void TimeEntryStore::clearStore()
{
QMutexLocker _{&m_mut};
TimeLight::logs::app()->trace("TimeEntryStore::clearStore() on thread {}: lock mutex", QThread::currentThreadId());
QMutexLocker _{&m_mutex};
beginResetModel();
m_store.clear();
endResetModel();
m_nextPage = m_user->manager()->paginationStartsAt();
m_isAtEnd = false;
TimeLight::logs::app()->trace("TimeEntryStore::clearStore() on thread {}: unlock mutex", QThread::currentThreadId());
}

void TimeEntryStore::insert(const TimeEntry &t)
{
QMutexLocker _{&m_mut};
if (auto it = std::find_if(m_store.begin(), m_store.end(), [&t](const auto &x) { return t.id() == x.id(); }); it != m_store.end())
TimeLight::logs::app()->trace("TimeEntryStore::insert() on thread {}: lock mutex", QThread::currentThreadId());
QMutexLocker _{&m_mutex};
if (auto it = std::find_if(static_begin(), static_end(), [&t](const auto &x) { return t.id() == x.id(); });
it != static_end())
*it = t;
else if (auto it = std::find_if(m_store.cbegin(), m_store.cend(), [&t](const auto &x) { return x.start() > t.start(); });
it != m_store.cend())
m_store.insert(it, t);
else if (auto it = std::find_if(static_cbegin(), static_cend(), [&t](const auto &x) { return x.start() > t.start(); });
it != static_cend())
{
beginInsertRows({}, it.index(), it.index());
m_store.insert(it.index(), t);
endInsertRows();
}
else if (t.start() > m_store.first().start())
{
beginInsertRows({}, 0, 0);
m_store.prepend(t);
endInsertRows();
}
else
{
beginInsertRows({}, m_store.size(), m_store.size());
m_store.append(t);
endInsertRows();
}
TimeLight::logs::app()->trace("TimeEntryStore::insert() on thread {}: unlock mutex", QThread::currentThreadId());
}

TimeEntryStore::iterator &TimeEntryStore::iterator::operator++()
{
m_index++;
maybeFetchMore();
return *this;
}

TimeEntryStore::iterator &TimeEntryStore::iterator::operator--()
{
m_index--;
return *this;
}

TimeEntryStore::iterator &TimeEntryStore::iterator::operator+(int i)
{
m_index += i;
maybeFetchMore();
return *this;
}

TimeEntryStore::iterator &TimeEntryStore::iterator::operator-(int i)
{
m_index -= i;
return *this;
}

bool TimeEntryStore::iterator::operator!=(const iterator &other)
{
return m_parent != other.m_parent || m_index != other.m_index;
}

bool TimeEntryStore::iterator::operator==(const iterator &other)
{
return m_parent == other.m_parent && m_index == other.m_index;
}

TimeEntryStore::iterator::iterator(qsizetype index, TimeEntryStore *store)
: m_index{index},
m_parent{store}
{
maybeFetchMore();
}

void TimeEntryStore::iterator::maybeFetchMore()
{
if (m_index == m_parent->m_store.size() && m_autoload && !m_parent->m_isAtEnd &&
m_parent->m_user->manager()->supportedPagination().testFlag(AbstractTimeServiceManager::Pagination::TimeEntries))
m_parent->fetchMore();
}

TimeEntryStore::const_iterator &TimeEntryStore::const_iterator::operator++()
{
m_index++;
maybeFetchMore();
return *this;
}

TimeEntryStore::const_iterator &TimeEntryStore::const_iterator::operator--()
{
m_index--;
return *this;
}

TimeEntryStore::const_iterator &TimeEntryStore::const_iterator::operator+(int i)
{
m_index += i;
maybeFetchMore();
return *this;
}

TimeEntryStore::const_iterator &TimeEntryStore::const_iterator::operator-(int i)
{
m_index -= i;
return *this;
}

bool TimeEntryStore::const_iterator::operator!=(const const_iterator &other)
{
return m_parent != other.m_parent || m_index != other.m_index;
}

bool TimeEntryStore::const_iterator::operator==(const const_iterator &other)
{
return m_parent == other.m_parent && m_index == other.m_index;
}

TimeEntryStore::const_iterator::const_iterator(qsizetype index, TimeEntryStore *store)
: m_index{index},
m_parent{store}
RangeSlice<TimeEntryStore::iterator> TimeEntryStore::sliceByDate(const QDateTime &oldest, const QDateTime &newest)
{
maybeFetchMore();
auto start = std::find_if(
begin(), end(), [&newest, &oldest](const TimeEntry &t) { return t.start() <= std::max(newest, oldest); });
auto finish = std::find_if(
start, end(), [&newest, &oldest](const TimeEntry &t) { return t.start() < std::min(newest, oldest); });
return {start, finish};
}

void TimeEntryStore::const_iterator::maybeFetchMore()
RangeSlice<TimeEntryStore::const_iterator> TimeEntryStore::constSliceByDate(const QDateTime &oldest, const QDateTime &newest)
{
if (m_index == m_parent->m_store.size() && m_autoload && !m_parent->m_isAtEnd &&
m_parent->m_user->manager()->supportedPagination().testFlag(AbstractTimeServiceManager::Pagination::TimeEntries))
m_parent->fetchMore();
auto start = std::find_if(
cbegin(), cend(), [&newest, &oldest](const TimeEntry &t) { return t.start() <= std::max(newest, oldest); });
auto finish = std::find_if(
start, cend(), [&newest, &oldest](const TimeEntry &t) { return t.start() < std::min(newest, oldest); });
return {start, finish};
}
Loading

0 comments on commit 292e293

Please sign in to comment.