You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tellico/src/fetch/isbndbfetcher.cpp

351 lines
11 KiB

/***************************************************************************
copyright : (C) 2006 by Robby Stephenson
email : robby@periapsis.org
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of version 2 of the GNU General Public License as *
* published by the Free Software Foundation; *
* *
***************************************************************************/
#include "isbndbfetcher.h"
#include "messagehandler.h"
#include "../translators/xslthandler.h"
#include "../translators/tellicoimporter.h"
#include "../tellico_kernel.h"
#include "../tellico_utils.h"
#include "../collection.h"
#include "../entry.h"
#include "../tellico_debug.h"
#include <klocale.h>
#include <kstandarddirs.h>
#include <kconfig.h>
#include <tqdom.h>
#include <tqlabel.h>
#include <tqlayout.h>
#include <tqfile.h>
namespace {
static const int ISBNDB_RETURNS_PER_REQUEST = 10;
static const int ISBNDB_MAX_RETURNS_TOTAL = 25;
static const char* ISBNDB_BASE_URL = "http://isbndb.com/api/books.xml";
static const char* ISBNDB_APP_ID = "3B9S3BQS";
}
using Tellico::Fetch::ISBNdbFetcher;
ISBNdbFetcher::ISBNdbFetcher(TQObject* parent_, const char* name_)
: Fetcher(parent_, name_), m_xsltHandler(0),
m_limit(ISBNDB_MAX_RETURNS_TOTAL), m_page(1), m_total(-1), m_countOffset(0),
m_job(0), m_started(false) {
}
ISBNdbFetcher::~ISBNdbFetcher() {
delete m_xsltHandler;
m_xsltHandler = 0;
}
TQString ISBNdbFetcher::defaultName() {
return i18n("ISBNdb.com");
}
TQString ISBNdbFetcher::source() const {
return m_name.isEmpty() ? defaultName() : m_name;
}
bool ISBNdbFetcher::canFetch(int type) const {
return type == Data::Collection::Book || type == Data::Collection::ComicBook || type == Data::Collection::Bibtex;
}
void ISBNdbFetcher::readConfigHook(const KConfigGroup& config_) {
Q_UNUSED(config_);
}
void ISBNdbFetcher::search(FetchKey key_, const TQString& value_) {
m_key = key_;
m_value = value_.stripWhiteSpace();
m_started = true;
m_page = 1;
m_total = -1;
m_numResults = 0;
m_countOffset = 0;
if(!canFetch(Kernel::self()->collectionType())) {
message(i18n("%1 does not allow searching for this collection type.").tqarg(source()), MessageHandler::Warning);
stop();
return;
}
doSearch();
}
void ISBNdbFetcher::continueSearch() {
m_started = true;
m_limit += ISBNDB_MAX_RETURNS_TOTAL;
doSearch();
}
void ISBNdbFetcher::doSearch() {
m_data.truncate(0);
// myDebug() << "ISBNdbFetcher::search() - value = " << value_ << endl;
KURL u(TQString::fromLatin1(ISBNDB_BASE_URL));
u.addQueryItem(TQString::fromLatin1("access_key"), TQString::fromLatin1(ISBNDB_APP_ID));
u.addQueryItem(TQString::fromLatin1("results"), TQString::fromLatin1("details,authors,subjects,texts"));
u.addQueryItem(TQString::fromLatin1("page_number"), TQString::number(m_page));
switch(m_key) {
case Title:
u.addQueryItem(TQString::fromLatin1("index1"), TQString::fromLatin1("title"));
u.addQueryItem(TQString::fromLatin1("value1"), m_value);
break;
case Person:
// yes, this also queries titles, too, it's a limitation of the isbndb api service
u.addQueryItem(TQString::fromLatin1("index1"), TQString::fromLatin1("combined"));
u.addQueryItem(TQString::fromLatin1("value1"), m_value);
break;
case Keyword:
u.addQueryItem(TQString::fromLatin1("index1"), TQString::fromLatin1("full"));
u.addQueryItem(TQString::fromLatin1("value1"), m_value);
break;
case ISBN:
u.addQueryItem(TQString::fromLatin1("index1"), TQString::fromLatin1("isbn"));
{
// only grab first value
TQString v = m_value.section(TQChar(';'), 0);
v.remove('-');
u.addQueryItem(TQString::fromLatin1("value1"), v);
}
break;
default:
kdWarning() << "ISBNdbFetcher::search() - key not recognized: " << m_key << endl;
stop();
return;
}
// myDebug() << "ISBNdbFetcher::search() - url: " << u.url() << endl;
m_job = KIO::get(u, false, false);
connect(m_job, TQT_SIGNAL(data(KIO::Job*, const TQByteArray&)),
TQT_SLOT(slotData(KIO::Job*, const TQByteArray&)));
connect(m_job, TQT_SIGNAL(result(KIO::Job*)),
TQT_SLOT(slotComplete(KIO::Job*)));
}
void ISBNdbFetcher::stop() {
if(!m_started) {
return;
}
// myDebug() << "ISBNdbFetcher::stop()" << endl;
if(m_job) {
m_job->kill();
m_job = 0;
}
m_data.truncate(0);
m_started = false;
emit signalDone(this);
}
void ISBNdbFetcher::slotData(KIO::Job*, const TQByteArray& data_) {
TQDataStream stream(m_data, IO_WriteOnly | IO_Append);
stream.writeRawBytes(data_.data(), data_.size());
}
void ISBNdbFetcher::slotComplete(KIO::Job* job_) {
// myDebug() << "ISBNdbFetcher::slotComplete()" << endl;
// since the fetch is done, don't worry about holding the job pointer
m_job = 0;
if(job_->error()) {
job_->showErrorDialog(Kernel::self()->widget());
stop();
return;
}
if(m_data.isEmpty()) {
myDebug() << "ISBNdbFetcher::slotComplete() - no data" << endl;
stop();
return;
}
#if 0
kdWarning() << "Remove debug from isbndbfetcher.cpp" << endl;
TQFile f(TQString::fromLatin1("/tmp/test.xml"));
if(f.open(IO_WriteOnly)) {
TQTextStream t(&f);
t.setEncoding(TQTextStream::UnicodeUTF8);
t << TQCString(m_data, m_data.size()+1);
}
f.close();
#endif
TQDomDocument dom;
if(!dom.setContent(m_data, false)) {
kdWarning() << "ISBNdbFetcher::slotComplete() - server did not return valid XML." << endl;
return;
}
if(m_total == -1) {
TQDomNode n = dom.documentElement().namedItem(TQString::fromLatin1("BookList"));
TQDomElement e = n.toElement();
if(!e.isNull()) {
m_total = e.attribute(TQString::fromLatin1("total_results"), TQString::number(-1)).toInt();
}
}
if(!m_xsltHandler) {
initXSLTHandler();
if(!m_xsltHandler) { // probably an error somewhere in the stylesheet loading
stop();
return;
}
}
// assume result is always utf-8
TQString str = m_xsltHandler->applyStylesheet(TQString::fromUtf8(m_data, m_data.size()));
Import::TellicoImporter imp(str);
Data::CollPtr coll = imp.collection();
int count = 0;
Data::EntryVec entries = coll->entries();
for(Data::EntryVec::Iterator entry = entries.begin(); m_numResults < m_limit && entry != entries.end(); ++entry, ++count) {
if(count < m_countOffset) {
continue;
}
if(!m_started) {
// might get aborted
break;
}
TQString desc = entry->field(TQString::fromLatin1("author"))
+ TQChar('/') + entry->field(TQString::fromLatin1("publisher"));
if(!entry->field(TQString::fromLatin1("cr_year")).isEmpty()) {
desc += TQChar('/') + entry->field(TQString::fromLatin1("cr_year"));
} else if(!entry->field(TQString::fromLatin1("pub_year")).isEmpty()){
desc += TQChar('/') + entry->field(TQString::fromLatin1("pub_year"));
}
SearchResult* r = new SearchResult(this, entry->title(), desc, entry->field(TQString::fromLatin1("isbn")));
m_entries.insert(r->uid, Data::EntryPtr(entry));
emit signalResultFound(r);
++m_numResults;
}
// are there any additional results to get?
m_hasMoreResults = m_page * ISBNDB_RETURNS_PER_REQUEST < m_total;
const int currentTotal = TQMIN(m_total, m_limit);
if(m_page * ISBNDB_RETURNS_PER_REQUEST < currentTotal) {
int foundCount = (m_page-1) * ISBNDB_RETURNS_PER_REQUEST + coll->entryCount();
message(i18n("Results from %1: %2/%3").tqarg(source()).tqarg(foundCount).tqarg(m_total), MessageHandler::Status);
++m_page;
m_countOffset = 0;
doSearch();
} else {
m_countOffset = m_entries.count() % ISBNDB_RETURNS_PER_REQUEST;
if(m_countOffset == 0) {
++m_page; // need to go to next page
}
stop(); // required
}
}
Tellico::Data::EntryPtr ISBNdbFetcher::fetchEntry(uint uid_) {
Data::EntryPtr entry = m_entries[uid_];
if(!entry) {
kdWarning() << "ISBNdbFetcher::fetchEntry() - no entry in dict" << endl;
return 0;
}
// if the publisher id is set, then we need to grab the real publisher name
const TQString id = entry->field(TQString::fromLatin1("pub_id"));
if(!id.isEmpty()) {
KURL u(TQString::fromLatin1(ISBNDB_BASE_URL));
u.setFileName(TQString::fromLatin1("publishers.xml"));
u.addQueryItem(TQString::fromLatin1("access_key"), TQString::fromLatin1(ISBNDB_APP_ID));
u.addQueryItem(TQString::fromLatin1("index1"), TQString::fromLatin1("publisher_id"));
u.addQueryItem(TQString::fromLatin1("value1"), id);
TQDomDocument dom = FileHandler::readXMLFile(u, true);
if(!dom.isNull()) {
TQString pub = dom.documentElement().namedItem(TQString::fromLatin1("PublisherList"))
.namedItem(TQString::fromLatin1("PublisherData"))
.namedItem(TQString::fromLatin1("Name"))
.toElement().text();
if(!pub.isEmpty()) {
entry->setField(TQString::fromLatin1("publisher"), pub);
}
}
entry->setField(TQString::fromLatin1("pub_id"), TQString());
}
return entry;
}
void ISBNdbFetcher::initXSLTHandler() {
TQString xsltfile = locate("appdata", TQString::fromLatin1("isbndb2tellico.xsl"));
if(xsltfile.isEmpty()) {
kdWarning() << "ISBNdbFetcher::initXSLTHandler() - can not locate isbndb2tellico.xsl." << endl;
return;
}
KURL u;
u.setPath(xsltfile);
delete m_xsltHandler;
m_xsltHandler = new XSLTHandler(u);
if(!m_xsltHandler->isValid()) {
kdWarning() << "ISBNdbFetcher::initXSLTHandler() - error in isbndb2tellico.xsl." << endl;
delete m_xsltHandler;
m_xsltHandler = 0;
return;
}
}
void ISBNdbFetcher::updateEntry(Data::EntryPtr entry_) {
// myDebug() << "ISBNdbFetcher::updateEntry()" << endl;
// limit to top 5 results
m_limit = 5;
TQString isbn = entry_->field(TQString::fromLatin1("isbn"));
if(!isbn.isEmpty()) {
search(Fetch::ISBN, isbn);
return;
}
// optimistically try searching for title and rely on Collection::sameEntry() to figure things out
TQString t = entry_->field(TQString::fromLatin1("title"));
if(!t.isEmpty()) {
m_limit = 10; // raise limit so more possibility of match
search(Fetch::Title, t);
return;
}
myDebug() << "ISBNdbFetcher::updateEntry() - insufficient info to search" << endl;
emit signalDone(this); // always need to emit this if not continuing with the search
}
Tellico::Fetch::ConfigWidget* ISBNdbFetcher::configWidget(TQWidget* parent_) const {
return new ISBNdbFetcher::ConfigWidget(parent_, this);
}
ISBNdbFetcher::ConfigWidget::ConfigWidget(TQWidget* parent_, const ISBNdbFetcher*/*=0*/)
: Fetch::ConfigWidget(parent_) {
TQVBoxLayout* l = new TQVBoxLayout(optionsWidget());
l->addWidget(new TQLabel(i18n("This source has no options."), optionsWidget()));
l->addStretch();
}
TQString ISBNdbFetcher::ConfigWidget::preferredName() const {
return ISBNdbFetcher::defaultName();
}
#include "isbndbfetcher.moc"