File indexing completed on 2024-05-12 05:10:12
0001 /*************************************************************************** 0002 Copyright (C) 2019 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 "librarythingimporter.h" 0026 #include "../collections/bookcollection.h" 0027 #include "../core/filehandler.h" 0028 #include "../utils/mapvalue.h" 0029 #include "../utils/isbnvalidator.h" 0030 #include "../tellico_debug.h" 0031 0032 #include <KLocalizedString> 0033 #include <KUrlRequester> 0034 0035 #include <QVBoxLayout> 0036 #include <QFormLayout> 0037 #include <QGroupBox> 0038 #include <QLabel> 0039 #include <QJsonDocument> 0040 #include <QJsonObject> 0041 #include <QJsonArray> 0042 #include <QJsonParseError> 0043 0044 using Tellico::Import::LibraryThingImporter; 0045 0046 LibraryThingImporter::LibraryThingImporter() : Import::Importer() 0047 , m_widget(nullptr) 0048 , m_URLRequester(nullptr) { 0049 } 0050 0051 LibraryThingImporter::LibraryThingImporter(const QUrl& url) : Import::Importer(url) 0052 , m_widget(nullptr) 0053 , m_URLRequester(nullptr) { 0054 } 0055 0056 bool LibraryThingImporter::canImport(int type) const { 0057 return type == Data::Collection::Book; 0058 } 0059 0060 Tellico::Data::CollPtr LibraryThingImporter::collection() { 0061 if(m_coll) { 0062 return m_coll; 0063 } 0064 0065 QUrl jsonUrl; 0066 if(m_widget && m_URLRequester) { 0067 jsonUrl = m_URLRequester->url(); 0068 } else if(!url().isEmpty()) { 0069 jsonUrl = url(); 0070 } else { 0071 myWarning() << "no widget!"; 0072 return Data::CollPtr(); 0073 } 0074 0075 if(jsonUrl.isEmpty() || !jsonUrl.isValid()) { 0076 myDebug() << "Bad jsonUrl:" << jsonUrl; 0077 return Data::CollPtr(); 0078 } 0079 0080 const QByteArray data = Tellico::FileHandler::readDataFile(jsonUrl, false /* quiet */); 0081 QJsonParseError parseError; 0082 QJsonDocument doc = QJsonDocument::fromJson(data, &parseError); 0083 if(doc.isNull()) { 0084 myDebug() << "Bad json data:" << parseError.errorString(); 0085 return Data::CollPtr(); 0086 } 0087 0088 QRegularExpression digits(QStringLiteral("\\d+")); 0089 QRegularExpression pubRx(QStringLiteral("^(.+?)[\\(,]")); 0090 0091 m_coll = new Data::BookCollection(true); 0092 bool defaultTitle = true; 0093 Data::EntryList entries; 0094 QVariantMap map = doc.object().toVariantMap(); 0095 QMapIterator<QString, QVariant> i(map); 0096 while(i.hasNext()) { 0097 i.next(); 0098 QVariantMap valueMap = i.value().toMap(); 0099 Data::EntryPtr entry(new Data::Entry(m_coll)); 0100 entry->setField(QStringLiteral("title"), mapValue(valueMap, "title")); 0101 entry->setField(QStringLiteral("pub_year"), mapValue(valueMap, "date")); 0102 entry->setField(QStringLiteral("keyword"), mapValue(valueMap, "tags")); 0103 entry->setField(QStringLiteral("genre"), mapValue(valueMap, "genre")); 0104 entry->setField(QStringLiteral("series"), mapValue(valueMap, "series")); 0105 entry->setField(QStringLiteral("language"), mapValue(valueMap, "language")); 0106 0107 const QString collName = mapValue(valueMap, "collections"); 0108 // default to using a collection title based on the first entry 0109 if(defaultTitle && !collName.isEmpty()) { 0110 defaultTitle = false; 0111 m_coll->setTitle(collName); 0112 } 0113 0114 const QString cdate = mapValue(valueMap, "entrydate"); 0115 if(!cdate.isEmpty()) { 0116 entry->setField(QStringLiteral("cdate"), cdate); 0117 } 0118 0119 QString pub = mapValue(valueMap, "publication"); 0120 // try to clean up the publisher 0121 QRegularExpressionMatch pubMatch = pubRx.match(pub); 0122 if(pubMatch.hasMatch()) { 0123 pub = pubMatch.captured(1).simplified(); 0124 } 0125 entry->setField(QStringLiteral("publisher"), pub); 0126 0127 QJsonArray authorArray = valueMap.value(QStringLiteral("authors")).toJsonArray(); 0128 QStringList authors; 0129 for(int i = 0; i < authorArray.size(); ++i) { 0130 QVariantMap m = authorArray.at(i).toObject().toVariantMap(); 0131 // TODO: read config option for author formatting? 0132 // use first-lastname for now 0133 const QString role = mapValue(m, "role"); 0134 if(role.isEmpty() || role == QLatin1String("Author")) { 0135 authors += mapValue(m, "fl"); 0136 } 0137 } 0138 entry->setField(QStringLiteral("author"), authors.join(FieldFormat::delimiterString())); 0139 0140 QJsonArray formatArray = valueMap.value(QStringLiteral("format")).toJsonArray(); 0141 if(!formatArray.isEmpty()) { 0142 // use the first one 0143 const QVariantMap m = formatArray.at(0).toObject().toVariantMap(); 0144 const QString format = mapValue(m, "text"); 0145 if(format == QLatin1String("Paperback")) { 0146 entry->setField(QStringLiteral("binding"), i18n("Paperback")); 0147 } else if(format == QLatin1String("Hardcover")) { 0148 entry->setField(QStringLiteral("binding"), i18n("Hardback")); 0149 } else { 0150 // just in case there's a value there 0151 entry->setField(QStringLiteral("binding"), format); 0152 } 0153 } 0154 0155 QString isbn = mapValue(valueMap, "originalisbn"); 0156 ISBNValidator::staticFixup(isbn); 0157 entry->setField(QStringLiteral("isbn"), isbn); 0158 0159 // grab first set of digits 0160 QRegularExpressionMatch match = digits.match(mapValue(valueMap, "pages")); 0161 if(match.hasMatch()) { 0162 entry->setField(QStringLiteral("pages"), match.captured(0)); 0163 } 0164 entries += entry; 0165 } 0166 m_coll->addEntries(entries); 0167 return m_coll; 0168 } 0169 0170 QWidget* LibraryThingImporter::widget(QWidget* parent_) { 0171 if(m_widget) { 0172 return m_widget; 0173 } 0174 m_widget = new QWidget(parent_); 0175 QVBoxLayout* l = new QVBoxLayout(m_widget); 0176 0177 QGroupBox* gbox = new QGroupBox(i18n("LibraryThing Options"), m_widget); 0178 QFormLayout* lay = new QFormLayout(gbox); 0179 0180 auto label = new QLabel(i18n("Export your LibraryThing collection in " 0181 "<a href=\"https://www.librarything.com/export.php?export_type=json\">JSON format</a>."), gbox); 0182 label->setOpenExternalLinks(true); 0183 lay->addRow(label); 0184 0185 m_URLRequester = new KUrlRequester(gbox); 0186 // these are in the old KDE4 filter format, not the Qt5 format 0187 QString filter = QLatin1String("*.json|") + i18n("JSON Files") 0188 + QLatin1Char('\n') 0189 + QLatin1String("*|") + i18n("All Files"); 0190 m_URLRequester->setFilter(filter); 0191 0192 lay->addRow(i18n("LibraryThing file:"), m_URLRequester); 0193 0194 l->addWidget(gbox); 0195 l->addStretch(1); 0196 0197 return m_widget; 0198 }