From 5ef8b38e55c1883224fe1f01f47aba45b7b42666 Mon Sep 17 00:00:00 2001 From: "Javier S. Pedro" Date: Mon, 1 Apr 2013 15:04:58 +0200 Subject: initial import --- forummodel.cpp | 281 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 281 insertions(+) create mode 100644 forummodel.cpp (limited to 'forummodel.cpp') diff --git a/forummodel.cpp b/forummodel.cpp new file mode 100644 index 0000000..43cb0bd --- /dev/null +++ b/forummodel.cpp @@ -0,0 +1,281 @@ +#include +#include + +#include "global.h" +#include "board.h" +#include "xmlrpcinterface.h" +#include "fetchtopicsaction.h" +#include "forummodel.h" + +ForumModel::ForumModel(QObject *parent) : + QAbstractListModel(parent), _boardUrl(), _board(0), _forumId(-1) +{ + QHash roles = roleNames(); + roles[TitleRole] = QByteArray("title"); + roles[IconRole] = QByteArray("icon"); + //roles[DescriptionRole] = QByteArray("subtitle"); + //roles[ForumIdRole] = QByteArray("forumId"); + //roles[ParentIdRole] = QByteArray("parentId"); + //roles[CategoryRole] = QByteArray("category"); + setRoleNames(roles); +} + +QString ForumModel::boardUrl() const +{ + return _boardUrl; +} + +void ForumModel::setBoardUrl(const QString &url) +{ + if (_boardUrl != url) { + disconnect(this, SLOT(handleForumTopicsChanged(int,int,int))); + clearModel(); + _board = 0; + + _boardUrl = url; + if (!_boardUrl.isEmpty()) { + _board = board_manager->getBoard(_boardUrl); + connect(_board, SIGNAL(forumTopicsChanged(int,int,int)), + SLOT(handleForumTopicsChanged(int,int,int))); + if (_forumId >= 0) { + update(); + reload(); + } + } + emit boardUrlChanged(); + } +} + +int ForumModel::forumId() const +{ + return _forumId; +} + +void ForumModel::setForumId(const int id) +{ + if (_forumId != id) { + clearModel(); + + _forumId = id; + + if (_forumId >= 0 && _board) { + update(); + reload(); + } + emit forumIdChanged(); + } +} + +int ForumModel::rowCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : _data.size(); +} + +QVariant ForumModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) return QVariant(); + const int row = index.row(); + if (row >= _data.size()) { + qWarning() << "Could not seek to" << row; + return QVariant(); + } + + switch (role) { + case TitleRole: + return _data[row].title; + break; + } + + return QVariant(); +} + +bool ForumModel::canFetchMore(const QModelIndex &parent) const +{ + if (parent.isValid() || !_board) return false; // Invalid state + return !_eof; +} + +void ForumModel::fetchMore(const QModelIndex &parent) +{ + if (parent.isValid()) return; + if (!_board) return; + if (_eof) return; + + const int start = _data.size(); + QList topics = loadTopics(start, start + FORUM_PAGE_SIZE - 1); + const int new_end = start + _data.size() - 1; + + if (topics.empty()) { + // We could not load anything more from DB! + _eof = true; + } else { + beginInsertRows(QModelIndex(), start, new_end); + _data.append(topics); + _eof = topics.size() < FORUM_PAGE_SIZE; // If short read, we reached EOF. + endInsertRows(); + } + + if (_board->service()->isAccessible()) { + if (!_data.empty()) { + QDateTime last = oldestPostUpdate(topics); + // If the topics we got from DB are too old, refresh online. + if (last.secsTo(QDateTime::currentDateTime()) > FORUM_TOPICS_TLL) { + qDebug() << "Fetching topics because of old"; + _board->enqueueAction(new FetchTopicsAction(_forumId, + start, + new_end, + _board)); + } + } + + // Try to fetch more topics if board is online and we reached the end of DB + if (_eof) { + qDebug() << "Fetching topics because of EOF"; + _board->enqueueAction(new FetchTopicsAction(_forumId, + _data.size(), + _data.size() + FORUM_PAGE_SIZE - 1, + _board)); + } + } +} + +QDateTime ForumModel::parseDateTime(const QVariant &v) +{ + QString s = v.toString(); + return QDateTime::fromString(s, Qt::ISODate); +} + +QDateTime ForumModel::oldestPostUpdate(const QList &topics) +{ + if (topics.empty()) return QDateTime::currentDateTime(); + QDateTime min = topics.first().last_update_time; + foreach (const Topic& topic, topics) { + if (min < topic.last_update_time) min = topic.last_update_time; + } + return min; +} + +QDateTime ForumModel::lastTopPostUpdate() +{ + if (!_board) return QDateTime(); + QSqlDatabase db = _board->database(); + QSqlQuery query(db); + query.prepare("SELECT last_update_time FROM topics " + "WHERE forum_id = :forum_id " + "ORDER BY last_reply_time DESC " + "LIMIT 1"); + query.bindValue(":forum_id", _forumId); + if (query.exec()) { + if (query.next()) { + return parseDateTime(query.value(0)); + } + } else { + qWarning() << "Could not fetch posts:" << query.lastError().text(); + } + return QDateTime(); +} + +QList ForumModel::loadTopics(int start, int end) +{ + Q_ASSERT(_board); + const int rows = end - start + 1; + QList topics; + QSqlQuery query(_board->database()); + query.prepare("SELECT topic_id, topic_title, last_reply_time, last_update_time FROM topics " + "WHERE forum_id = :forum_id " + "ORDER by last_reply_time DESC " + "LIMIT :start, :limit"); + query.bindValue(":forum_id", _forumId); + query.bindValue(":start", start); + query.bindValue(":limit", rows); + if (query.exec()) { + topics.reserve(rows); + while (query.next()) { + Topic topic; + topic.topic_id = query.value(0).toInt(); + topic.title = query.value(1).toString(); + topic.last_reply_time = parseDateTime(query.value(2)); + topic.last_update_time = parseDateTime(query.value(3)); + topics.append(topic); + } + } else { + qWarning() << "Could not load topics:" << query.lastError().text(); + } + return topics; +} + +void ForumModel::clearModel() +{ + beginResetModel(); + _eof = false; + _data.clear(); + endResetModel(); +} + +void ForumModel::handleForumTopicsChanged(int forumId, int start, int end) +{ + if (forumId == _forumId) { + // Yep, our topics list changed. + qDebug() << "My topics changed" << start << end; + if (end > _data.size()) { + // If for any reason we have more topics now, it means we might + // no longer be EOF... + _eof = false; + } + if (start > _data.size() + 1) { + // We are still not interested into these topics. + qDebug() << "Topics too far"; + return; + } + + QList topics = loadTopics(start, end); + if (topics.size() < end - start + 1) { + _eof = true; // Short read + end = start + topics.size() - 1; + } + + if (end >= _data.size()) { + qDebug() << "Call insert rows" << _data.size() << end; + beginInsertRows(QModelIndex(), _data.size(), end); + _data.reserve(end + 1); + for (int i = start; i < _data.size(); i++) { + _data[i] = topics[i - start]; + } + for (int i = _data.size(); i <= end; i++) { + Q_ASSERT(i >= start); + _data.append(topics[i - start]); + } + endInsertRows(); + emit dataChanged(createIndex(start, 0), createIndex(_data.size() - 1, 0)); + } else { + qDebug() << "Just refresh the data"; + for (int i = start; i < end; i++) { + _data[i] = topics[i - start]; + } + emit dataChanged(createIndex(start, 0), createIndex(end, 0)); + } + } +} + +void ForumModel::update() +{ + if (!_board || _forumId < 0) return; + // Start by requesting an update of the first 20 topics + if (!_board->service()->isAccessible()) return; + QDateTime last = lastTopPostUpdate(); + if (!last.isValid() || + last.secsTo(QDateTime::currentDateTime()) > FORUM_TOP_TLL) { + // Outdated or empty, refresh. + _board->enqueueAction(new FetchTopicsAction(_forumId, 0, FORUM_PAGE_SIZE - 1, _board)); + } else { + qDebug() << "Topics not outdated"; + } +} + +void ForumModel::reload() +{ + Q_ASSERT(_data.empty()); + Q_ASSERT(!_eof); + // Fetch an initial bunch of topics + fetchMore(); +} -- cgit v1.2.3