/*
* @copyright 2012 Philip Warner
* @license GNU General Public License
*
* This file is part of Book Catalogue.
*
* Book Catalogue is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Book Catalogue is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Book Catalogue. If not, see <http://www.gnu.org/licenses/>.
*/
package com.eleybourn.bookcatalogue.goodreads;
import net.philipwarner.taskqueue.QueueManager;
import android.content.Context;
import android.database.Cursor;
import com.eleybourn.bookcatalogue.BcQueueManager;
import com.eleybourn.bookcatalogue.BookCatalogueApp;
import com.eleybourn.bookcatalogue.BookEvents.GrNoIsbnEvent;
import com.eleybourn.bookcatalogue.BookEvents.GrNoMatchEvent;
import com.eleybourn.bookcatalogue.BooksCursor;
import com.eleybourn.bookcatalogue.BooksRowView;
import com.eleybourn.bookcatalogue.CatalogueDBAdapter;
import com.eleybourn.bookcatalogue.R;
import com.eleybourn.bookcatalogue.goodreads.GoodreadsManager.Exceptions.NotAuthorizedException;
import com.eleybourn.bookcatalogue.goodreads.GoodreadsManager.ExportDisposition;
import com.eleybourn.bookcatalogue.utils.Logger;
import com.eleybourn.bookcatalogue.utils.Utils;
/**
* Background task class to send all books in the database to goodreads.
*
* @author Philip Warner
*/
public class SendAllBooksTask extends GenericTask {
private static final long serialVersionUID = -1933000305276643875L;
/** Last book ID processed */
private long mLastId = 0;
/** Flag indicating if it should only send UPDATED books to goodreads; false == all books */
private final boolean mUpdatesOnly;
/** Number of books with no ISBN */
private int mNoIsbn = 0;
/** Number of books that had ISBN but could not be found */
private int mNotFound = 0;
/** Number of books successfully sent */
private int mSent = 0;
/** Total count of books processed */
private int mCount = 0;
/** Total count of books that are in cursor */
private int mTotalBooks = 0;
/**
* Constructor
*/
public SendAllBooksTask(boolean updatesOnly) {
super(BookCatalogueApp.getResourceString(R.string.send_books_to_goodreads));
mUpdatesOnly = updatesOnly;
}
/**
* Run the task, log exceptions.
*/
@Override
public boolean run(QueueManager manager, Context c) {
boolean result = false;
try {
result = sendAllBooks(manager, c);
} catch (Exception e) {
Logger.logError(e, "Error sending books to GoodReads");
}
return result;
}
/**
* Do the mean of the task. Deal with restarts by using mLastId as starting point.
*
* @param qmanager
* @param context
* @return
*
* @throws NotAuthorizedException
*/
public boolean sendAllBooks(QueueManager qmanager, Context context) throws NotAuthorizedException {
//int lastSave = mCount;
boolean needsRetryReset = true;
// ENHANCE: Work out a way of checking if GR site is up
//if (!Utils.hostIsAvailable(context, "www.goodreads.com"))
// return false;
if (!Utils.isNetworkAvailable(context)) {
// Only wait 5 mins max on network errors.
if (getRetryDelay() > 300)
setRetryDelay(300);
return false;
}
// Get the app context; the underlying activity may go away. And get DB.
GoodreadsManager grManager = new GoodreadsManager();
Context ctx = context.getApplicationContext();
CatalogueDBAdapter dbHelper = new CatalogueDBAdapter(ctx.getApplicationContext());
// Ensure we are allowed
if (!grManager.hasValidCredentials()) {
throw new NotAuthorizedException(null);
}
dbHelper.open();
BooksCursor books = null;
Cursor shelves = null;
try {
books = dbHelper.getAllBooksForGoodreadsCursor(mLastId, mUpdatesOnly);
final BooksRowView book = books.getRowView();
mTotalBooks = books.getCount() + mCount;
while (books.moveToNext()) {
// Try to export one book
ExportDisposition disposition;
Exception exportException = null;
try {
disposition = grManager.sendOneBook(dbHelper, book);
} catch (Exception e) {
disposition = ExportDisposition.error;
exportException = e;
}
// Handle the result
switch(disposition) {
case error:
this.setException(exportException);
qmanager.saveTask(this);
return false;
case sent:
// Record the change
dbHelper.setGoodreadsSyncDate(books.getId());
mSent++;
break;
case noIsbn:
storeEvent( new GrNoIsbnEvent(book.getId()) );
mNoIsbn++;
break;
case notFound:
storeEvent( new GrNoMatchEvent(book.getId()) );
mNotFound++;
break;
case networkError:
// Only wait 5 mins on network errors.
if (getRetryDelay() > 300)
setRetryDelay(300);
qmanager.saveTask(this);
return false;
}
// Update internal status
mCount++;
mLastId = books.getId();
// If we have done one successfully, reset the counter so a subsequent network error does not result in a long delay
if (needsRetryReset) {
needsRetryReset = false;
resetRetryCounter();
}
// Save every few rows in case phone dies (and to allow task queries to see data)
// Actually, save every row because it updates the UI, and sending a row takes a while.
//if (mCount - lastSave >= 5) {
// qmanager.saveTask(this);
// lastSave = mCount;
//}
qmanager.saveTask(this);
if (this.isAborting()) {
qmanager.saveTask(this);
return false;
}
}
} finally {
if (books != null)
try {
books.close();
} catch (Exception e)
{
// Ignore failures, but log them
Logger.logError(e, "Failed to close GoodReads books cursor");
}
if (shelves != null)
try {
shelves.close();
} catch (Exception e)
{
// Ignore failures, but log them
Logger.logError(e, "Failed to close GoodReads book bookshelves cursor");
}
try {
dbHelper.close();
} catch(Exception e)
{}
}
// Notify the user: '15 books processed: 3 sent successfully, 5 with no ISBN and 7 with ISBN but not found in goodreads'
String s = context.getString(R.string.send_all_to_goodreads_result, mCount, mSent, mNoIsbn, mNotFound);
BookCatalogueApp.showNotification(R.id.NOTIFICATION,
context.getString(R.string.send_books_to_goodreads), s,
BookCatalogueApp.getAppToForegroundIntent(context));
return true;
}
/**
* Make a more informative description
*/
@Override
public String getDescription() {
String base = super.getDescription();
return base + " (" + BookCatalogueApp.getResourceString(R.string.x_of_y, mCount, mTotalBooks);
}
@Override
public long getCategory() {
return BcQueueManager.CAT_GOODREADS_EXPORT_ALL;
}
}