From: APTX Date: Sun, 3 Jun 2012 15:23:29 +0000 (+0200) Subject: PendingMyListUpdate for handling MyList edits (except for voting) X-Git-Url: https://gitweb.aptx.org/?a=commitdiff_plain;h=6163e359b51a97e64573c97688e53ed043adb37e;p=localmylist.git PendingMyListUpdate for handling MyList edits (except for voting) --- diff --git a/localmylist/database.cpp b/localmylist/database.cpp index a21e1e6..22a8793 100644 --- a/localmylist/database.cpp +++ b/localmylist/database.cpp @@ -83,6 +83,19 @@ PendingRequest::PendingRequest() size = 0; } +PendingMyListUpdate::PendingMyListUpdate() +{ + fid = 0; + myState = 0; + myFileState = 0; + setMyWatched = false; + setMyState = false; + setMyFileState = false; + setMyStorage = false; + setMySource = false; + setMyOther = false; +} + HostInfo::HostInfo() { id = 0; @@ -126,6 +139,11 @@ struct DatabaseInternal QSqlQuery getRequestBatchQuery; QSqlQuery clearRequestQuery; + QSqlQuery addPendingMyListUpdateQuery; + QSqlQuery getPendingMyListUpdateQuery; + QSqlQuery getPendingMyListUpdateBatchQuery; + QSqlQuery clearPendingMyListUpdateQuery; + QThread *thread; }; @@ -656,6 +674,98 @@ bool Database::clearRequest(const PendingRequest &request) return ret; } +bool Database::addPendingMyListUpdate(const PendingMyListUpdate &request) +{ + d->addPendingMyListUpdateQuery.bindValue(":fid", request.fid); + d->addPendingMyListUpdateQuery.bindValue(":setMyWatched", request.setMyWatched); + d->addPendingMyListUpdateQuery.bindValue(":myWatched", request.myWatched); + d->addPendingMyListUpdateQuery.bindValue(":setMyState", request.setMyState); + d->addPendingMyListUpdateQuery.bindValue(":myState", request.myState); + d->addPendingMyListUpdateQuery.bindValue(":setMyFileState", request.setMyFileState); + d->addPendingMyListUpdateQuery.bindValue(":myFileState", request.myFileState); + d->addPendingMyListUpdateQuery.bindValue(":setMyStorage", request.setMyStorage); + d->addPendingMyListUpdateQuery.bindValue(":myStorage", request.myStorage); + d->addPendingMyListUpdateQuery.bindValue(":setMySource", request.setMySource); + d->addPendingMyListUpdateQuery.bindValue(":mySource", request.mySource); + d->addPendingMyListUpdateQuery.bindValue(":setMyOther", request.setMyOther); + d->addPendingMyListUpdateQuery.bindValue(":myOther", request.myOther); + + return exec(d->addPendingMyListUpdateQuery); +} + +PendingMyListUpdate Database::getPendingMyListUpdate(qint64 updateId) +{ + d->getPendingMyListUpdateQuery.bindValue(":updateId", updateId); + + PendingMyListUpdate request; + + if (!exec(d->getPendingMyListUpdateQuery)) + return request; + + if (d->getPendingMyListUpdateQuery.next()) + { + request.updateId = d->getPendingMyListUpdateQuery.value(0).toLongLong(); + request.fid = d->getPendingMyListUpdateQuery.value(1).toInt(); + request.setMyWatched = d->getPendingMyListUpdateQuery.value(2).toBool(); + request.myWatched = d->getPendingMyListUpdateQuery.value(3).toDateTime(); + request.setMyState = d->getPendingMyListUpdateQuery.value(4).toBool(); + request.myState = d->getPendingMyListUpdateQuery.value(5).toInt(); + request.setMyFileState = d->getPendingMyListUpdateQuery.value(6).toBool(); + request.myFileState = d->getPendingMyListUpdateQuery.value(7).toInt(); + request.setMyStorage = d->getPendingMyListUpdateQuery.value(8).toBool(); + request.myStorage = d->getPendingMyListUpdateQuery.value(9).toString(); + request.setMySource = d->getPendingMyListUpdateQuery.value(10).toBool(); + request.mySource = d->getPendingMyListUpdateQuery.value(11).toString(); + request.setMyOther = d->getPendingMyListUpdateQuery.value(12).toBool(); + request.myOther = d->getPendingMyListUpdateQuery.value(13).toString(); + request.added = d->getPendingMyListUpdateQuery.value(14).toDateTime(); + request.started = d->getPendingMyListUpdateQuery.value(15).toDateTime(); + } + return request; +} + +QList Database::getPendingMyListUpdateBatch(int limit) +{ + d->getPendingMyListUpdateBatchQuery.bindValue(":limit", limit); + + QList ret; + + if (!exec(d->getPendingMyListUpdateBatchQuery)) + return ret; + + while (d->getPendingMyListUpdateBatchQuery.next()) + { + PendingMyListUpdate request; + request.updateId = d->getPendingMyListUpdateBatchQuery.value(0).toLongLong(); + request.fid = d->getPendingMyListUpdateBatchQuery.value(1).toInt(); + request.setMyWatched = d->getPendingMyListUpdateBatchQuery.value(2).toBool(); + request.myWatched = d->getPendingMyListUpdateBatchQuery.value(3).toDateTime(); + request.setMyState = d->getPendingMyListUpdateBatchQuery.value(4).toBool(); + request.myState = d->getPendingMyListUpdateBatchQuery.value(5).toInt(); + request.setMyFileState = d->getPendingMyListUpdateBatchQuery.value(6).toBool(); + request.myFileState = d->getPendingMyListUpdateBatchQuery.value(7).toInt(); + request.setMyStorage = d->getPendingMyListUpdateBatchQuery.value(8).toBool(); + request.myStorage = d->getPendingMyListUpdateBatchQuery.value(9).toString(); + request.setMySource = d->getPendingMyListUpdateBatchQuery.value(10).toBool(); + request.mySource = d->getPendingMyListUpdateBatchQuery.value(11).toString(); + request.setMyOther = d->getPendingMyListUpdateBatchQuery.value(12).toBool(); + request.myOther = d->getPendingMyListUpdateBatchQuery.value(13).toString(); + request.added = d->getPendingMyListUpdateBatchQuery.value(14).toDateTime(); + request.started = d->getPendingMyListUpdateBatchQuery.value(15).toDateTime(); + ret << request; + } + + d->getPendingMyListUpdateBatchQuery.finish(); + + return ret; +} + +bool Database::clearPendingMyListUpdate(const PendingMyListUpdate &request) +{ + d->clearPendingMyListUpdateQuery.bindValue(":update_id", request.updateId); + return exec(d->clearPendingMyListUpdateQuery); +} + bool Database::truncateTitleData() { return exec("TRUNCATE TABLE anime_title"); @@ -856,7 +966,32 @@ void Database::prepareQueries() d->clearRequestQuery = QSqlQuery(d->db); d->clearRequestQuery.prepare("DELETE FROM pending_request WHERE aid = :aid AND eid = :eid AND fid = :fid AND ed2k = :ed2k AND size = :size"); + d->addPendingMyListUpdateQuery = QSqlQuery(d->db); + d->addPendingMyListUpdateQuery.prepare("INSERT INTO pending_mylist_update VALUES(DEFAULT, :fid, :setMyWatched, :myWatched, " + ":setMyState, :myState, :setMyFileState, :myFileState, :setMyStorage, :myStorage, " + ":setMySource, :mySource, :setMyOther, :myOther, DEFAULT, DEFAULT, DEFAULT)"); + + d->getPendingMyListUpdateBatchQuery = QSqlQuery(d->db); + d->getPendingMyListUpdateBatchQuery.prepare("UPDATE pending_mylist_update SET started = NOW() " + "WHERE update_id IN (SELECT update_id FROM pending_mylist_update " + " WHERE started IS NULL " + " ORDER BY added ASC " + " LIMIT :limit) " + "RETURNING update_id, fid, set_my_watched, my_watched, set_my_state, " + "my_state, set_my_file_state, my_file_state, set_my_storage, my_storage, " + "set_my_source, my_source, set_my_other, my_other, added, started"); + + d->getPendingMyListUpdateQuery = QSqlQuery(d->db); + d->getPendingMyListUpdateQuery.prepare("SELECT update_id, fid, set_my_watched, my_watched, set_my_state, my_state, " + "set_my_file_state, my_file_state, set_my_storage, my_storage, set_my_source, " + "my_source, set_my_other, my_other, added, started, finished " + "FROM pending_mylist_update WHERE update_id = :updateId"); + + d->clearPendingMyListUpdateQuery = QSqlQuery(d->db); + d->clearPendingMyListUpdateQuery.prepare("UPDATE pending_mylist_update SET finished = NOW() WHERE update_id = :updateId"); + d->db.driver()->subscribeToNotification("new_pending_request"); + d->db.driver()->subscribeToNotification("new_pending_mylist_update"); } bool Database::exec(QSqlQuery &query) @@ -910,6 +1045,10 @@ void Database::handleNotification(const QString ¬ification) { emit newPendingRequest(); } + else if (notification == "new_pending_mylist_update") + { + emit newPendingMyListUpdate(); + } } } // namespace LocalMyList diff --git a/localmylist/database.h b/localmylist/database.h index 015d81f..99d8dcf 100644 --- a/localmylist/database.h +++ b/localmylist/database.h @@ -147,6 +147,30 @@ struct LOCALMYLISTSHARED_EXPORT PendingRequest PendingRequest(); }; +struct LOCALMYLISTSHARED_EXPORT PendingMyListUpdate +{ + qint64 updateId; + int fid; + bool setMyWatched; + QDateTime myWatched; + bool setMyState; + int myState; + bool setMyFileState; + int myFileState; + bool setMyStorage; + QString myStorage; + bool setMySource; + QString mySource; + bool setMyOther; + QString myOther; + + QDateTime added; + QDateTime started; + QDateTime finished; + + PendingMyListUpdate(); +}; + struct LOCALMYLISTSHARED_EXPORT HostInfo { int id; @@ -212,6 +236,11 @@ public: QList getRequestBatch(int limit = 10); bool clearRequest(const PendingRequest &request); + bool addPendingMyListUpdate(const PendingMyListUpdate &request); + PendingMyListUpdate getPendingMyListUpdate(qint64 updateId); + QList getPendingMyListUpdateBatch(int limit = 10); + bool clearPendingMyListUpdate(const PendingMyListUpdate &request); + bool truncateTitleData(); bool truncateMyListData(); bool truncateDatabase(); @@ -229,6 +258,7 @@ signals: void disconnected(); void newPendingRequest(); + void newPendingMyListUpdate(); private slots: void handleNotification(const QString ¬ification); diff --git a/localmylist/mylist.cpp b/localmylist/mylist.cpp index c54c391..17bedf5 100644 --- a/localmylist/mylist.cpp +++ b/localmylist/mylist.cpp @@ -71,9 +71,21 @@ Settings *MyList::settings() const return m_settings; } - // ------- +void MyList::markWatched(int fid, QDateTime when) +{ + if (!fid) + return; + + PendingMyListUpdate request; + request.fid = fid; + request.setMyWatched = true; + request.myWatched = when; + db->addPendingMyListUpdate(request); +} + + void MyList::setupUdpClient() { using namespace ::AniDBUdpClient; @@ -93,6 +105,7 @@ void MyList::setupRequestHandler() m_requestHandler = new RequestHandler(db, this); connect(db, SIGNAL(newPendingRequest()), m_requestHandler, SLOT(handleRequests())); + connect(db, SIGNAL(newPendingMyListUpdate()), m_requestHandler, SLOT(handleMyListUpdates())); } void MyList::loadLocalSettings() diff --git a/localmylist/mylist.h b/localmylist/mylist.h index b426a7b..3cb32b6 100644 --- a/localmylist/mylist.h +++ b/localmylist/mylist.h @@ -9,6 +9,7 @@ #include #include #include +#include namespace LocalMyList { @@ -35,6 +36,7 @@ public: public slots: void setHostName(QString name); + void markWatched(int fid, QDateTime when = QDateTime::currentDateTime()); AbstractTask *addFile(const QFileInfo &file); AbstractTask *addDirectory(const QDir &directory); diff --git a/localmylist/requesthandler.cpp b/localmylist/requesthandler.cpp index 3944478..6adb711 100644 --- a/localmylist/requesthandler.cpp +++ b/localmylist/requesthandler.cpp @@ -19,6 +19,7 @@ RequestHandler::RequestHandler(Database *db, QObject *parent) : { this->db = db; connect(this, SIGNAL(batchFinished()), this, SLOT(handleRequests()), Qt::QueuedConnection); + connect(this, SIGNAL(myListUpdateBatchFinished()), this, SLOT(handleMyListUpdates()), Qt::QueuedConnection); } void RequestHandler::handleRequests() @@ -110,6 +111,48 @@ void RequestHandler::handleRequests() emit batchFinished(); } +void RequestHandler::handleMyListUpdates() +{ + using namespace ::AniDBUdpClient; + + qDebug() << "handleMyListUpdates"; + + db->transaction(); + QList requests = db->getPendingMyListUpdateBatch(); + + qDebug() << "Got" << requests.count() << "requests"; + + foreach (const PendingMyListUpdate &request, requests) + { + MyListAddCommand cmd(request.fid, true); + + if (request.setMyWatched) + { + if (request.myWatched.isNull()) + { + cmd.setViewed(false); + } + else + { + cmd.setViewed(true); + cmd.setViewDate(request.myWatched); + } + } + if (request.setMyState) + cmd.setState(State(request.myState)); + if (request.setMyStorage) + cmd.setStorage(request.myStorage); + if (request.setMySource) + cmd.setSource(request.mySource); + if (request.setMyOther) + cmd.setOther(request.myOther); + + MyListAddReply *reply = Client::instance()->send(cmd); + connect(reply, SIGNAL(replyReady(bool)), this, SLOT(myListEditReplyRecieved(bool))); + myListUpdateIdMap.insert(reply, request.updateId); + } +} + void RequestHandler::animeRequestComplete(bool success) { using namespace ::AniDBUdpClient; @@ -420,8 +463,60 @@ void RequestHandler::myListAddReplyRecieved(bool success) ed2kRequest.ed2k = reply->command().ed2k(); ed2kRequest.size = reply->command().size(); db->clearRequest(ed2kRequest); +} +void RequestHandler::myListEditReplyRecieved(bool success) +{ + using namespace ::AniDBUdpClient; + + MyListAddReply *reply = qobject_cast(sender()); + Q_ASSERT(reply); + Q_ASSERT(myListUpdateIdMap.contains(reply)); + reply->deleteLater(); + + qint64 id = myListUpdateIdMap.take(reply); + + if (!success) + return; + + db->transaction(); + File file = db->getFile(reply->command().fid()); + + if (!file.fid) + { + qWarning("Updated file not in DB"); + return; + } + + PendingMyListUpdate request = db->getPendingMyListUpdate(id); + + if (!request.updateId) + { + qWarning("PendingMyListUpdate not in DB"); + return; + } + + if (request.setMyWatched) + file.myWatched = request.myWatched; + if (request.setMyState) + file.myState = request.myState; + if (request.setMyFileState) + file.myFileState = request.myFileState; + if (request.setMySource) + file.mySource = request.mySource.isNull() ? QString("") : request.mySource; + if (request.setMyStorage) + file.myStorage = request.myStorage.isNull() ? QString("") : request.myStorage; + if (request.setMyOther) + file.myOther = request.myOther.isNull() ? QString("") : request.myOther; + + file.myUpdate = QDateTime::currentDateTime(); + + db->setFile(file); + + qDebug() << "Clearing update_id" << id; + db->clearPendingMyListUpdate(request); + db->commit(); } } // namespace LocalMyList diff --git a/localmylist/requesthandler.h b/localmylist/requesthandler.h index 994fa40..8f2222f 100644 --- a/localmylist/requesthandler.h +++ b/localmylist/requesthandler.h @@ -25,20 +25,24 @@ public: signals: void batchFinished(); + void myListUpdateBatchFinished(); public slots: void handleRequests(); + void handleMyListUpdates(); void animeRequestComplete(bool success); void episodeRequestComplete(bool success); void fileRequestComplete(bool success); void voteRequestComplete(bool); void myListAddReplyRecieved(bool success); + void myListEditReplyRecieved(bool success); private: Database *db; QMap< ::AniDBUdpClient::VoteReply *, int> idMap; + QMap< ::AniDBUdpClient::MyListAddReply *, qint64> myListUpdateIdMap; }; } // namespace LocalMyList diff --git a/localmylist/share/schema/schema.sql b/localmylist/share/schema/schema.sql index b4a6ae8..8e2af8e 100644 --- a/localmylist/share/schema/schema.sql +++ b/localmylist/share/schema/schema.sql @@ -18,9 +18,9 @@ CREATE TABLE anime ( my_vote numeric(4,2), my_vote_date timestamp without time zone, my_temp_vote numeric(4,2), - my_temp_vote_date timestamp without time zone + my_temp_vote_date timestamp without time zone, + CONSTRAINT aid_pk PRIMARY KEY (aid) ); -ALTER TABLE ONLY anime ADD CONSTRAINT aid_pk PRIMARY KEY (aid); CREATE INDEX rating_idx ON anime USING btree (rating); CREATE INDEX temp_rating_idx ON anime USING btree (temp_rating); CREATE INDEX my_vote_idx ON anime USING btree (my_vote); @@ -30,9 +30,9 @@ CREATE TABLE anime_title ( aid integer NOT NULL, type integer DEFAULT 1, language character(8) DEFAULT ''::bpchar, - title character varying(500) NOT NULL + title character varying(500) NOT NULL, + CONSTRAINT unique_title UNIQUE (aid, type, language, title) ); -ALTER TABLE ONLY anime_title ADD CONSTRAINT unique_title UNIQUE (aid, type, language, title); CREATE INDEX aid_idx ON anime_title USING btree (aid); CREATE INDEX title_idx ON anime_title USING gin (to_tsvector('simple'::regconfig, (title)::text)); CREATE INDEX language_idx ON anime_title USING hash (language); @@ -57,9 +57,9 @@ CREATE TABLE episode ( rating numeric(4,2), votes integer, my_vote numeric(4,2), - my_vote_date timestamp without time zone + my_vote_date timestamp without time zone, + CONSTRAINT eid_pk PRIMARY KEY (eid) ); -ALTER TABLE ONLY episode ADD CONSTRAINT eid_pk PRIMARY KEY (eid); CREATE INDEX episode_aid_fk ON episode USING btree (aid); CREATE TABLE file ( @@ -94,9 +94,9 @@ CREATE TABLE file ( my_file_state integer, my_storage text, my_source text, - my_other text + my_other text, + CONSTRAINT fid_pk PRIMARY KEY (fid) ); -ALTER TABLE ONLY file ADD CONSTRAINT fid_pk PRIMARY KEY (fid); CREATE INDEX file_aid_fk ON file USING btree (aid); CREATE INDEX file_eid_fk ON file USING btree (eid); @@ -104,9 +104,9 @@ CREATE TABLE file_episode_rel ( fid integer NOT NULL, eid integer NOT NULL, start_percent integer, - end_percent integer + end_percent integer, + CONSTRAINT fid_eid_pk PRIMARY KEY (fid, eid) ); -ALTER TABLE ONLY file_episode_rel ADD CONSTRAINT fid_eid_pk PRIMARY KEY (fid, eid); CREATE INDEX file_episode_rel_eid_fk ON file_episode_rel USING btree (eid); CREATE INDEX file_episode_rel_fid_fk ON file_episode_rel USING btree (fid); @@ -120,22 +120,31 @@ CREATE TABLE unknown_file ( ed2k character(32) NOT NULL, size bigint NOT NULL, host_id integer, - path text + path text, + CONSTRAINT unknown_files_pk PRIMARY KEY (ed2k, size) ); -ALTER TABLE ONLY unknown_file ADD CONSTRAINT unknown_files_pk PRIMARY KEY (ed2k, size); -CREATE TABLE pending_mylist_update ( - fid integer NOT NULL, - my_watched timestamp without time zone, - my_state integer, - my_file_state integer, - my_storage text, - my_source text, - my_other text, - added timestamp without time zone DEFAULT now(), - start timestamp without time zone +CREATE TABLE pending_mylist_update +( + update_id bigserial NOT NULL, + fid integer NOT NULL, + set_my_watched boolean NOT NULL DEFAULT false, + my_watched timestamp without time zone, + set_my_state boolean NOT NULL DEFAULT false, + my_state integer, + set_my_file_state boolean NOT NULL DEFAULT false, + my_file_state integer, + set_my_storage boolean NOT NULL DEFAULT false, + my_storage text, + set_my_source boolean NOT NULL DEFAULT false, + my_source text, + set_my_other boolean NOT NULL DEFAULT false, + my_other text, + added timestamp without time zone DEFAULT now(), + started timestamp without time zone, + finished timestamp without time zone, + CONSTRAINT pending_mylist_update_pk PRIMARY KEY (update_id ) ); -ALTER TABLE ONLY pending_mylist_update ADD CONSTRAINT pending_mylist_update_pk PRIMARY KEY (fid); CREATE TABLE pending_request ( aid integer DEFAULT 0 NOT NULL, @@ -145,9 +154,9 @@ CREATE TABLE pending_request ( size bigint DEFAULT 0 NOT NULL, priority integer DEFAULT 1 NOT NULL, added timestamp without time zone DEFAULT now(), - start timestamp without time zone + start timestamp without time zone, + CONSTRAINT pending_request_pk PRIMARY KEY (aid, eid, fid, ed2k, size) ); -ALTER TABLE ONLY pending_request ADD CONSTRAINT pending_request_pk PRIMARY KEY (aid, eid, fid, ed2k, size); CREATE INDEX pending_request_added_idx ON pending_request USING btree (added); CREATE INDEX pending_request_priority_idx ON pending_request USING btree (priority, added, start); CREATE INDEX pending_request_start_idx ON pending_request USING btree (start); @@ -155,25 +164,24 @@ CREATE INDEX pending_request_start_idx ON pending_request USING btree (start); CREATE TABLE config ( key character varying(250) NOT NULL, value text, - is_user_facing boolean DEFAULT false NOT NULL + is_user_facing boolean DEFAULT false NOT NULL, + CONSTRAINT config_pk PRIMARY KEY (key) ); -ALTER TABLE ONLY config ADD CONSTRAINT config_pk PRIMARY KEY (key); - CREATE TABLE host ( host_id serial NOT NULL, name character varying(100), - is_udp_host boolean DEFAULT false + is_udp_host boolean DEFAULT false, + CONSTRAINT host_pk PRIMARY KEY (host_id), + CONSTRAINT host_unique_name UNIQUE (name) ); -ALTER TABLE ONLY host ADD CONSTRAINT host_pk PRIMARY KEY (host_id); -ALTER TABLE ONLY host ADD CONSTRAINT host_unique_name UNIQUE (name); CREATE TABLE log ( log_id serial NOT NULL, type integer, - log text + log text, + CONSTRAINT log_pk PRIMARY KEY (log_id) ); -ALTER TABLE ONLY log ADD CONSTRAINT log_pk PRIMARY KEY (log_id); CREATE VIEW file_data AS SELECT f.fid, f.eid, f.aid, f.gid, f.anidb_update, f.entry_update, f.my_update, f.ed2k, f.size, f.length, f.extension, f.group_name, f.group_name_short, f.crc, f.release_date, f.version, f.censored, f.type, f.quality_id, f.quality, f.resolution, f.video_codec, f.audio_codec, f.audio_language, f.subtitle_language, f.aspect_ratio, f.my_watched, f.my_state, f.my_file_state, f.my_storage, f.my_source, f.my_other, a.title_romaji AS atitle, e.title_english AS eptitle FROM ((file f LEFT JOIN anime a ON ((f.aid = a.aid))) LEFT JOIN episode e ON ((f.eid = e.eid))); @@ -193,3 +201,4 @@ CREATE RULE pending_request_ignore_duplicate AS ON INSERT TO pending_request WHE CREATE RULE unknown_file_ignore_duplicate AS ON INSERT TO unknown_file WHERE (EXISTS (SELECT 1 FROM unknown_file WHERE ((unknown_file.ed2k = new.ed2k) AND (unknown_file.size = new.size)))) DO INSTEAD NOTHING; COMMENT ON RULE unknown_file_ignore_duplicate ON unknown_file IS 'Adding the same file more than once can happen'; +CREATE RULE new_pending_mylist_update_rule AS ON INSERT TO pending_mylist_update DO NOTIFY new_pending_mylist_update; \ No newline at end of file