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 }