summaryrefslogtreecommitdiff
path: root/fetchtopicsaction.cpp
blob: 13aebddeede3e24e2f9ca173e88206b0c78922ec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#include <QtCore/QDateTime>
#include <QtCore/QDebug>
#include <QtSql/QSqlDatabase>
#include <QtSql/QSqlQuery>

#include "global.h"
#include "board.h"
#include "xmlrpcinterface.h"
#include "xmlrpcreply.h"
#include "fetchtopicsaction.h"

FetchTopicsAction::FetchTopicsAction(int forumId, int start, int end, Board *board) :
    Action(board), _forumId(forumId), _start(start), _end(end)
{
}

bool FetchTopicsAction::isSupersetOf(Action *action) const
{
	FetchTopicsAction *other = qobject_cast<FetchTopicsAction*>(action);
	if (other) {
		if (other->_forumId == _forumId) {
			if (_end == FetchAllTopics) {
				// If this is fetching all topics, then this is a superset
				// of every other action... except those that also fetch all
				// topics.
				return other->_end != FetchAllTopics;
			} else if (other->_end == FetchAllTopics) {
				// If the other action fetches all topics, this cannot be a
				// superset
				return false;
			} else if (_start <= other->_start && _end >= other->_end) {
				// Otherwise, check if the range of posts fetched by this
				// action fully contains the other action.
				return true;
			}
		}
	}
	return false;
}

void FetchTopicsAction::execute()
{
	int end = _end;
	if (end == FetchAllTopics) {
		// Fetch topics in blocks of size 50.
		end = _start + MAX_FORUM_PAGE_SIZE;
		// After finishing this action, a new one will be enqueued with the next 50.
	}

	_call = _board->service()->asyncCall("get_topic",
										 QString::number(_forumId), _start, end);
	_call->setParent(this);
	connect(_call, SIGNAL(finished(XmlRpcPendingCall*)), SLOT(handleFinishedCall()));
}

void FetchTopicsAction::handleFinishedCall()
{
	XmlRpcReply<QVariantMap> result(_call);
	if (result.isValid()) {
		QVariantMap map = result;
		QVariantList topics = map["topics"].toList();
		QSqlDatabase db = _board->database();
		db.transaction();

		QSqlQuery query(db);
		query.prepare("INSERT OR REPLACE INTO topics (forum_id, topic_id, topic_title, topic_author_id, topic_author_name, is_subscribed, is_closed, icon_url, last_reply_time, reply_number, new_post, position, last_update_time) "
		              "VALUES (:forum_id, :topic_id, :topic_title, :topic_author_id, :topic_author_name, :is_subscribed, :is_closed, :icon_url, :last_reply_time, :reply_number, :new_post, :position, :last_update_time)");

		Q_ASSERT(_start >= 0);
		int position = _start;

		foreach (const QVariant& topic_v, topics) {
			QVariantMap topic = topic_v.toMap();
			bool ok = false;
			int forum_id = topic["forum_id"].toInt(&ok);
			if (!ok) {
				// Not fatal, just use the one we expected.
				forum_id = _forumId;
			}
			int topic_id = topic["topic_id"].toInt(&ok);
			if (!ok) {
				qWarning() << "No topic_id in" << topic;
				continue;
			}

			query.bindValue(":forum_id", forum_id);
			query.bindValue(":topic_id", topic_id);
			query.bindValue(":topic_title", unencodeTopicText(topic["topic_title"]));
			query.bindValue(":topic_author_id", topic["topic_author_id"].toInt());
			query.bindValue(":topic_author_name", unencodeTopicText(topic["topic_author_name"]));
			query.bindValue(":is_subscribed", topic["is_subscribed"].toBool() ? 1 : 0);
			query.bindValue(":is_closed", topic["is_closed"].toBool() ? 1 : 0);
			query.bindValue(":icon_url", topic["icon_url"].toString());
			query.bindValue(":last_reply_time", topic["last_reply_time"].toDateTime());
			query.bindValue(":reply_number", topic["reply_number"].toInt());
			query.bindValue(":new_post", topic["new_post"].toBool() ? 1 : 0);
			query.bindValue(":position", position);
			query.bindValue(":last_update_time", QDateTime::currentDateTime());

			position++;

			if (!query.exec()) {
				qWarning() << "Failed to store topic info for:" << topic_id;
				handleDatabaseError("storing topic info", query);
				continue;
			}
		}

		db.commit();
		if (!topics.isEmpty()) {
			Q_ASSERT(_start >= 0);
			Q_ASSERT(position - 1 >= _start);
			_board->notifyForumTopicsChanged(_forumId,
			                                 _start, position - 1);
		}
		if (_end == FetchAllTopics && topics.size() == MAX_FORUM_PAGE_SIZE) {
			// Ok, let's prepare to fetch the next block of topics because
			// there are probably more of them
			int next_start = _start + MAX_FORUM_PAGE_SIZE;
			_board->enqueueAction(new FetchTopicsAction(_forumId,
			                                            next_start,
			                                            FetchAllTopics,
			                                            _board));
		}
	} else {
		qWarning() << "Could not fetch topics";
		// TODO emit error ...
	}
	emit finished(this);
	_call->deleteLater();
}

QString FetchTopicsAction::unencodeTopicText(const QVariant &v)
{
	QByteArray ba = v.toByteArray();
	return QString::fromUtf8(ba.constData(), ba.length());
}