File indexing completed on 2024-05-12 05:09:39

0001 /***************************************************************************
0002     Copyright (C) 2014-2020 Robby Stephenson <robby@periapsis.org>
0003  ***************************************************************************/
0004 
0005 /***************************************************************************
0006  *                                                                         *
0007  *   This program is free software; you can redistribute it and/or         *
0008  *   modify it under the terms of the GNU General Public License as        *
0009  *   published by the Free Software Foundation; either version 2 of        *
0010  *   the License or (at your option) version 3 or any later version        *
0011  *   accepted by the membership of KDE e.V. (or its successor approved     *
0012  *   by the membership of KDE e.V.), which shall act as a proxy            *
0013  *   defined in Section 14 of version 3 of the license.                    *
0014  *                                                                         *
0015  *   This program is distributed in the hope that it will be useful,       *
0016  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0017  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0018  *   GNU General Public License for more details.                          *
0019  *                                                                         *
0020  *   You should have received a copy of the GNU General Public License     *
0021  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
0022  *                                                                         *
0023  ***************************************************************************/
0024 
0025 #include "mrlookupfetcher.h"
0026 #include "../translators/bibteximporter.h"
0027 #include "../collections/bibtexcollection.h"
0028 #include "../utils/guiproxy.h"
0029 #include "../utils/string_utils.h"
0030 #include "../tellico_debug.h"
0031 
0032 #include <KLocalizedString>
0033 #include <KIO/Job>
0034 #include <KIO/JobUiDelegate>
0035 #include <KConfigGroup>
0036 #include <KJobWidgets/KJobWidgets>
0037 
0038 #include <QLabel>
0039 #include <QVBoxLayout>
0040 #include <QFile>
0041 #include <QTextCodec>
0042 #include <QUrlQuery>
0043 
0044 namespace {
0045   static const char* MRLOOKUP_URL = "https://mathscinet.ams.org/mrlookup";
0046 }
0047 
0048 using namespace Tellico;
0049 using Tellico::Fetch::MRLookupFetcher;
0050 
0051 MRLookupFetcher::MRLookupFetcher(QObject* parent_)
0052     : Fetcher(parent_)
0053     , m_started(false) {
0054 }
0055 
0056 MRLookupFetcher::~MRLookupFetcher() {
0057 }
0058 
0059 QString MRLookupFetcher::source() const {
0060   return m_name.isEmpty() ? defaultName() : m_name;
0061 }
0062 
0063 bool MRLookupFetcher::canSearch(Fetch::FetchKey k) const {
0064   return k == Title || k == Person;
0065 }
0066 
0067 bool MRLookupFetcher::canFetch(int type) const {
0068   return type == Data::Collection::Bibtex;
0069 }
0070 
0071 void MRLookupFetcher::readConfigHook(const KConfigGroup& config_) {
0072   Q_UNUSED(config_);
0073 }
0074 
0075 void MRLookupFetcher::search() {
0076   m_started = true;
0077 
0078   QUrl u(QString::fromLatin1(MRLOOKUP_URL));
0079   QUrlQuery q;
0080   switch(request().key()) {
0081     case Title:
0082       q.addQueryItem(QStringLiteral("ti"), request().value());
0083       break;
0084 
0085     case Person:
0086       q.addQueryItem(QStringLiteral("au"), request().value());
0087       break;
0088 
0089     default:
0090       myWarning() << source() << "- key not recognized:" << request().key();
0091       stop();
0092       return;
0093   }
0094   q.addQueryItem(QStringLiteral("format"), QStringLiteral("bibtex"));
0095   u.setQuery(q);
0096 
0097 //  myDebug() << u;
0098   m_job = KIO::storedGet(u, KIO::NoReload, KIO::HideProgressInfo);
0099   KJobWidgets::setWindow(m_job, GUI::Proxy::widget());
0100   connect(m_job.data(), &KJob::result, this, &MRLookupFetcher::slotComplete);
0101 }
0102 
0103 void MRLookupFetcher::stop() {
0104   if(!m_started) {
0105     return;
0106   }
0107   if(m_job) {
0108     m_job->kill();
0109     m_job = nullptr;
0110   }
0111   m_started = false;
0112   emit signalDone(this);
0113 }
0114 
0115 Tellico::Data::EntryPtr MRLookupFetcher::fetchEntryHook(uint uid_) {
0116   return m_entries[uid_];
0117 }
0118 
0119 Tellico::Fetch::FetchRequest MRLookupFetcher::updateRequest(Data::EntryPtr entry_) {
0120   QString title = entry_->field(QStringLiteral("title"));
0121   if(!title.isEmpty()) {
0122     return FetchRequest(Title, title);
0123   }
0124   return FetchRequest();
0125 }
0126 
0127 void MRLookupFetcher::slotComplete(KJob* job_) {
0128   KIO::StoredTransferJob* job = static_cast<KIO::StoredTransferJob*>(job_);
0129 
0130   if(job->error()) {
0131     job->uiDelegate()->showErrorMessage();
0132     stop();
0133     return;
0134   }
0135 
0136   QByteArray data = job->data();
0137   if(data.isEmpty()) {
0138     myDebug() << "no data";
0139     stop();
0140     return;
0141   }
0142   // see bug 319662. If fetcher is cancelled, job is killed
0143   // if the pointer is retained, it gets double-deleted
0144   m_job = nullptr;
0145 
0146 #if 0
0147   myWarning() << "Remove debug from mrlookup.cpp";
0148   QFile f(QString::fromLatin1("/tmp/test-mrlookup.html"));
0149   if(f.open(QIODevice::WriteOnly)) {
0150     QTextStream t(&f);
0151     t.setCodec("UTF-8");
0152     t << data;
0153   }
0154   f.close();
0155 #endif
0156   const QString text = QString::fromUtf8(data.constData(), data.size());
0157   QString bibtexString;
0158 
0159   // grab everything within the <pre></pre> block
0160   static const QRegularExpression preRx(QLatin1String("<pre>(.+?)</pre>"), QRegularExpression::DotMatchesEverythingOption);
0161   QRegularExpressionMatchIterator i = preRx.globalMatch(text);
0162   while(i.hasNext()) {
0163     QRegularExpressionMatch match = i.next();
0164     bibtexString += match.captured(1);
0165   }
0166   if(bibtexString.isEmpty()) {
0167     myDebug() << "no bibtex response";
0168     stop();
0169     return;
0170   }
0171 #if 0
0172   myWarning() << "Remove debug from mrlookup.cpp";
0173   QFile f(QString::fromLatin1("/tmp/test.bibtex"));
0174   if(f.open(QIODevice::WriteOnly)) {
0175     QTextStream t(&f);
0176     t.setCodec("UTF-8");
0177     t << bibtexString;
0178   }
0179   f.close();
0180 #endif
0181 
0182   Import::BibtexImporter imp(bibtexString);
0183   // quiet warnings...
0184   imp.setCurrentCollection(Data::CollPtr(new Data::BibtexCollection(true)));
0185   Data::CollPtr coll = imp.collection();
0186   if(!coll) {
0187     myDebug() << "no collection pointer";
0188     stop();
0189     return;
0190   }
0191 
0192   // switch the FJournal field with the Journal field
0193   foreach(Data::EntryPtr entry, coll->entries()) {
0194     entry->setField(QStringLiteral("journal"), entry->field(QStringLiteral("fjournal")));
0195   }
0196   coll->removeField(QStringLiteral("fjournal"));
0197   // unnecessary fields
0198   coll->removeField(QStringLiteral("mrclass"));
0199   coll->removeField(QStringLiteral("mrnumber"));
0200   coll->removeField(QStringLiteral("mrreviewer"));
0201 
0202   foreach(Data::EntryPtr entry, coll->entries()) {
0203     if(!m_started) {
0204       // might get aborted
0205       break;
0206     }
0207 
0208     FetchResult* r = new FetchResult(this, entry);
0209     m_entries.insert(r->uid, Data::EntryPtr(entry));
0210     emit signalResultFound(r);
0211   }
0212 //  m_hasMoreResults = m_start <= m_total;
0213   m_hasMoreResults = false; // for now, no continued searches
0214 
0215   stop(); // required
0216 }
0217 
0218 Tellico::Fetch::ConfigWidget* MRLookupFetcher::configWidget(QWidget* parent_) const {
0219   return new MRLookupFetcher::ConfigWidget(parent_, this);
0220 }
0221 
0222 QString MRLookupFetcher::defaultName() {
0223   return QStringLiteral("Mathematical Reviews");
0224 }
0225 
0226 QString MRLookupFetcher::defaultIcon() {
0227   return favIcon("http://www.ams.org");
0228 }
0229 
0230 MRLookupFetcher::ConfigWidget::ConfigWidget(QWidget* parent_, const MRLookupFetcher* /*=0*/)
0231     : Fetch::ConfigWidget(parent_) {
0232   QVBoxLayout* l = new QVBoxLayout(optionsWidget());
0233   l->addWidget(new QLabel(i18n("This source has no options."), optionsWidget()));
0234   l->addStretch();
0235 }
0236 
0237 QString MRLookupFetcher::ConfigWidget::preferredName() const {
0238   return MRLookupFetcher::defaultName();
0239 }