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 }