File indexing completed on 2024-05-12 15:27:48

0001 /***************************************************************************
0002     File                 : ImportDatasetWidget.cpp
0003     Project              : LabPlot
0004     Description          : import online dataset widget
0005     --------------------------------------------------------------------
0006     Copyright            : (C) 2019 Kovacs Ferencz (kferike98@gmail.com)
0007     Copyright            : (C) 2019 by Alexander Semke (alexander.semke@web.de)
0008 
0009 ***************************************************************************/
0010 
0011 /***************************************************************************
0012  *                                                                         *
0013  *  This program is free software; you can redistribute it and/or modify   *
0014  *  it under the terms of the GNU General Public License as published by   *
0015  *  the Free Software Foundation; either version 2 of the License, or      *
0016  *  (at your option) any later version.                                    *
0017  *                                                                         *
0018  *  This program is distributed in the hope that it will be useful,        *
0019  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
0020  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
0021  *  GNU General Public License for more details.                           *
0022  *                                                                         *
0023  *   You should have received a copy of the GNU General Public License     *
0024  *   along with this program; if not, write to the Free Software           *
0025  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
0026  *   Boston, MA  02110-1301  USA                                           *
0027  *                                                                         *
0028  ***************************************************************************/
0029 
0030 #include "backend/datasources/DatasetHandler.h"
0031 #include "kdefrontend/datasources/ImportDatasetWidget.h"
0032 #include "kdefrontend/DatasetModel.h"
0033 
0034 #include <QCompleter>
0035 #include <QDir>
0036 #include <QFile>
0037 #include <QJsonDocument>
0038 #include <QJsonValue>
0039 #include <QMessageBox>
0040 #include <QNetworkAccessManager>
0041 #include <QNetworkReply>
0042 #include <QStandardPaths>
0043 
0044 #include <KConfigGroup>
0045 #include <KSharedConfig>
0046 #include <KLocalizedString>
0047 
0048 /*!
0049     \class ImportDatasetWidget
0050     \brief Widget for importing data from a dataset.
0051 
0052     \ingroup kdefrontend
0053  */
0054 ImportDatasetWidget::ImportDatasetWidget(QWidget* parent) : QWidget(parent),
0055     m_networkManager(new QNetworkAccessManager(this)) {
0056 
0057     ui.setupUi(this);
0058 
0059     m_jsonDir = QStandardPaths::locate(QStandardPaths::AppDataLocation, QLatin1String("datasets"), QStandardPaths::LocateDirectory);
0060     loadCategories();
0061 
0062     ui.lwDatasets->setSelectionMode(QAbstractItemView::SingleSelection);
0063     ui.twCategories->setSelectionMode(QAbstractItemView::SingleSelection);
0064 
0065     const int size = ui.leSearch->height();
0066     ui.lSearch->setPixmap( QIcon::fromTheme(QLatin1String("edit-find")).pixmap(size, size) );
0067 
0068     QString info = i18n("Enter the keyword you want to search for");
0069     ui.lSearch->setToolTip(info);
0070     ui.leSearch->setToolTip(info);
0071     ui.leSearch->setPlaceholderText(i18n("Search..."));
0072     ui.leSearch->setFocus();
0073 
0074     connect(ui.cbCollections, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
0075             this, &ImportDatasetWidget::collectionChanged);
0076     connect(ui.twCategories, &QTreeWidget::itemDoubleClicked, this, &ImportDatasetWidget::updateDatasets);
0077     connect(ui.twCategories, &QTreeWidget::itemSelectionChanged, [this] {
0078         if (!m_initializing)
0079             updateDatasets(ui.twCategories->selectedItems().first());
0080     });
0081 
0082     connect(ui.leSearch, &QLineEdit::textChanged, this, &ImportDatasetWidget::updateCategories);
0083     connect(ui.lwDatasets, &QListWidget::itemSelectionChanged, [this]() {
0084         if (!m_initializing)
0085             datasetChanged();
0086     });
0087     connect(ui.lwDatasets, &QListWidget::doubleClicked, [this]() {emit datasetDoubleClicked(); });
0088     connect(m_networkManager, &QNetworkAccessManager::finished, this, &ImportDatasetWidget::downloadFinished);
0089 
0090     //select the last used collection
0091     KConfigGroup conf(KSharedConfig::openConfig(), "ImportDatasetWidget");
0092     const QString& collection = conf.readEntry("Collection", QString());
0093     if (collection.isEmpty())
0094         ui.cbCollections->setCurrentIndex(0);
0095     else {
0096         for (int i = 0; i < ui.cbCollections->count(); ++i) {
0097             if (ui.cbCollections->itemData(i).toString() == collection) {
0098                 ui.cbCollections->setCurrentIndex(i);
0099                 break;
0100             }
0101         }
0102     }
0103 }
0104 
0105 ImportDatasetWidget::~ImportDatasetWidget() {
0106     delete m_model;
0107 
0108     //save the selected collection
0109     if (ui.cbCollections->currentIndex() != -1) {
0110         KConfigGroup conf(KSharedConfig::openConfig(), "ImportDatasetWidget");
0111         conf.writeEntry("Collection", ui.cbCollections->itemData(ui.cbCollections->currentIndex()).toString());
0112     }
0113 }
0114 
0115 /**
0116  * @brief Processes the json metadata file that contains the list of categories and subcategories and their datasets.
0117  */
0118 void ImportDatasetWidget::loadCategories() {
0119     m_datasetsMap.clear();
0120     ui.cbCollections->clear();
0121 
0122     const QString collectionsFileName = m_jsonDir + QLatin1String("/DatasetCollections.json");
0123     QFile file(collectionsFileName);
0124 
0125     if (file.open(QIODevice::ReadOnly)) {
0126         QJsonDocument document = QJsonDocument::fromJson(file.readAll());
0127         file.close();
0128         if (!document.isArray()) {
0129             QDEBUG("Invalid definition of " + collectionsFileName);
0130             return;
0131         }
0132 
0133         m_collections = document.array();
0134 
0135         for (const QJsonValueRef col : m_collections) {
0136             const QJsonObject& collection = col.toObject();
0137             const QString& m_collection = collection[QLatin1String("name")].toString();
0138 
0139             QString path = m_jsonDir + QLatin1Char('/') + m_collection + ".json";
0140             QFile collectionFile(path);
0141             if (collectionFile.open(QIODevice::ReadOnly)) {
0142                 QJsonDocument collectionDocument = QJsonDocument::fromJson(collectionFile.readAll());
0143                 if (!collectionDocument.isObject()) {
0144                     QDEBUG("Invalid definition of " + path);
0145                     continue;
0146                 }
0147 
0148                 const QJsonObject& collectionObject = collectionDocument.object();
0149                 const QJsonArray& categoryArray = collectionObject.value(QLatin1String("categories")).toArray();
0150 
0151                 //processing categories
0152                 for (const auto& cat : categoryArray) {
0153                     const QJsonObject& currentCategory = cat.toObject();
0154                     const QString& categoryName = currentCategory.value(QLatin1String("name")).toString();
0155                     const QJsonArray& subcategories = currentCategory.value(QLatin1String("subcategories")).toArray();
0156 
0157                     //processing subcategories
0158                     for (const auto& sub : subcategories) {
0159                         QJsonObject currentSubCategory = sub.toObject();
0160                         QString subcategoryName = currentSubCategory.value(QLatin1String("name")).toString();
0161                         const QJsonArray& datasetArray = currentSubCategory.value(QLatin1String("datasets")).toArray();
0162 
0163                         //processing the datasets of the actual subcategory
0164                         for (const auto& dataset : datasetArray)
0165                             m_datasetsMap[m_collection][categoryName][subcategoryName].push_back(dataset.toObject().value(QLatin1String("filename")).toString());
0166                     }
0167                 }
0168             }
0169         }
0170 
0171         if (m_model)
0172             delete m_model;
0173         m_model = new DatasetModel(m_datasetsMap);
0174 
0175         //Fill up collections combo box
0176         ui.cbCollections->addItem(i18n("All") + QLatin1String(" (") + QString::number(m_model->allDatasetsList().toStringList().size()) + QLatin1Char(')'));
0177         for (const QString& collection : m_model->collections())
0178             ui.cbCollections->addItem(collection + QLatin1String(" (") + QString::number(m_model->datasetCount(collection)) + QLatin1Char(')'), collection);
0179 
0180         collectionChanged(ui.cbCollections->currentIndex());
0181     } else
0182         QMessageBox::critical(this, i18n("File not found"),
0183                               i18n("Couldn't open the dataset collections file %1. Please check your installation.", collectionsFileName));
0184 }
0185 
0186 /**
0187  * Shows all categories and sub-categories for the currently selected collection
0188  */
0189 void ImportDatasetWidget::collectionChanged(int index) {
0190     m_allCollections = (index == 0);
0191 
0192     if (!m_allCollections)
0193         m_collection = ui.cbCollections->itemData(index).toString();
0194     else
0195         m_collection = "";
0196 
0197     //update the info field
0198     QString info;
0199     if (!m_allCollections) {
0200         for (const QJsonValueRef col : m_collections) {
0201             const QJsonObject& collection = col.toObject();
0202             if ( m_collection == collection[QLatin1String("name")].toString() ) {
0203                 info += collection[QLatin1String("description")].toString();
0204                 info += QLatin1String("<br><br></hline><br><br>");
0205                 break;
0206             }
0207         }
0208     } else {
0209         for (const QJsonValueRef col : m_collections) {
0210             const QJsonObject& collection = col.toObject();
0211             info += collection[QLatin1String("description")].toString();
0212             info += QLatin1String("<br><br>");
0213         }
0214     }
0215     ui.lInfo->setText(info);
0216     updateCategories();
0217 
0218     //update the completer
0219     if (m_completer)
0220         delete m_completer;
0221 
0222     //add all categories, sub-categories and the dataset names for the current collection
0223     QStringList keywords;
0224     for (const auto& category : m_model->categories(m_collection)) {
0225         keywords << category;
0226         for (const auto& subcategory : m_model->subcategories(m_collection, category)) {
0227             keywords << subcategory;
0228             for (const QString& dataset : m_model->datasets(m_collection, category, subcategory))
0229                 keywords << dataset;
0230         }
0231     }
0232 
0233     m_completer = new QCompleter(keywords, this);
0234     m_completer->setCompletionMode(QCompleter::PopupCompletion);
0235     m_completer->setCaseSensitivity(Qt::CaseSensitive);
0236     ui.leSearch->setCompleter(m_completer);
0237 }
0238 
0239 void ImportDatasetWidget::updateCategories() {
0240     m_initializing = true;
0241     ui.twCategories->clear();
0242 
0243     auto* rootItem = new QTreeWidgetItem(QStringList(i18n("All")));
0244     ui.twCategories->addTopLevelItem(rootItem);
0245 
0246     const QString& filter = ui.leSearch->text();
0247 
0248     //add categories
0249     for (const auto& category : m_model->categories(m_collection)) {
0250         const bool categoryMatch = (filter.isEmpty() || category.startsWith(filter, Qt::CaseInsensitive));
0251 
0252         if (categoryMatch) {
0253             auto* const item = new QTreeWidgetItem(QStringList(category));
0254             rootItem->addChild(item);
0255 
0256             //add all sub-categories
0257             for (const auto& subcategory : m_model->subcategories(m_collection, category))
0258                 item->addChild(new QTreeWidgetItem(QStringList(subcategory)));
0259         } else {
0260             QTreeWidgetItem* item = nullptr;
0261             for (const auto& subcategory : m_model->subcategories(m_collection, category)) {
0262                 bool subcategoryMatch = subcategory.startsWith(filter, Qt::CaseInsensitive);
0263 
0264                 if (subcategoryMatch) {
0265                     if (!item) {
0266                         item = new QTreeWidgetItem(QStringList(category));
0267                         rootItem->addChild(item);
0268                         item->setExpanded(true);
0269                     }
0270                     item->addChild(new QTreeWidgetItem(QStringList(subcategory)));
0271                 } else {
0272                     for (const QString& dataset : m_model->datasets(m_collection, category, subcategory)) {
0273                         bool datasetMatch = dataset.startsWith(filter, Qt::CaseInsensitive);
0274                         if (datasetMatch) {
0275                             if (!item) {
0276                                 item = new QTreeWidgetItem(QStringList(category));
0277                                 rootItem->addChild(item);
0278                                 item->setExpanded(true);
0279                             }
0280                             item->addChild(new QTreeWidgetItem(QStringList(subcategory)));
0281                             break;
0282                         }
0283                     }
0284                 }
0285             }
0286         }
0287     }
0288 
0289     //remote the root item "All" if nothing has matched to the filter string
0290     if (rootItem->childCount() == 0)
0291         ui.twCategories->clear();
0292 
0293 
0294     //expand the root item and select the first category item
0295     rootItem->setExpanded(true);
0296     if (filter.isEmpty()) {
0297         rootItem->setSelected(true);
0298         updateDatasets(rootItem);
0299     } else {
0300         if (rootItem->child(0) && rootItem->child(0)->child(0)) {
0301             rootItem->child(0)->child(0)->setSelected(true);
0302             updateDatasets(rootItem->child(0)->child(0));
0303         } else
0304             updateDatasets(nullptr);
0305     }
0306 
0307     m_initializing = false;
0308 }
0309 
0310 /**
0311  * @brief Populates lwDatasets with the datasets of the selected subcategory or its parent
0312  * @param item the selected subcategory
0313  */
0314 void ImportDatasetWidget::updateDatasets(QTreeWidgetItem* item) {
0315     m_initializing = true;
0316     ui.lwDatasets->clear();
0317 
0318     if (!item) {
0319         //no category item is selected because nothing matches the search string
0320         m_initializing = false;
0321         datasetChanged();
0322         return;
0323     }
0324 
0325     const QString& filter = ui.leSearch->text();
0326 
0327     if (item->childCount() == 0) {
0328         //sub-category was selected -> show all its datasets
0329         m_category = item->parent()->text(0);
0330         m_subcategory = item->text(0);
0331 
0332         addDatasetItems(m_collection, m_category, m_subcategory, filter);
0333     } else {
0334         if (!item->parent()) {
0335             //top-level item "All" was selected -> show datasets for all categories and their sub-categories
0336             m_category = "";
0337             m_subcategory = "";
0338 
0339             for (const auto& category : m_model->categories(m_collection)) {
0340                 for (const auto& subcategory : m_model->subcategories(m_collection, category))
0341                     addDatasetItems(m_collection, category, subcategory, filter);
0342             }
0343         } else {
0344             //a category was selected -> show all its datasets
0345             m_category = item->text(0);
0346             m_subcategory = "";
0347 
0348             for (const auto& subcategory : m_model->subcategories(m_collection, m_category))
0349                 addDatasetItems(m_collection, m_category, subcategory, filter);
0350         }
0351     }
0352 
0353     m_initializing = false;
0354 
0355     //select the first available dataset
0356     if (ui.lwDatasets->count())
0357         ui.lwDatasets->setCurrentRow(0);
0358 }
0359 
0360 void ImportDatasetWidget::addDatasetItems(const QString& collection, const QString& category, const QString& subcategory, const QString& filter) {
0361     if (!filter.isEmpty() &&
0362         (category.startsWith(filter, Qt::CaseInsensitive) || subcategory.startsWith(filter, Qt::CaseInsensitive))) {
0363 
0364         for (const QString& dataset : m_model->datasets(collection, category, subcategory))
0365             ui.lwDatasets->addItem(new QListWidgetItem(dataset));
0366     } else {
0367         for (const QString& dataset : m_model->datasets(collection, category, subcategory)) {
0368             if (filter.isEmpty() || dataset.startsWith(filter, Qt::CaseInsensitive))
0369                 ui.lwDatasets->addItem(new QListWidgetItem(dataset));
0370         }
0371     }
0372 }
0373 
0374 /**
0375  * @brief Returns the name of the selected dataset
0376  */
0377 QString ImportDatasetWidget::getSelectedDataset() const {
0378     if (!ui.lwDatasets->selectedItems().isEmpty())
0379         return ui.lwDatasets->selectedItems().at(0)->text();
0380     else
0381         return QString();
0382 }
0383 
0384 /**
0385  * @brief Initiates the processing of the dataset's metadata file and of the dataset itself.
0386  * @param datasetHandler the DatasetHanlder that downloads processes the dataset
0387  */
0388 void ImportDatasetWidget::import(DatasetHandler* datasetHandler) {
0389     datasetHandler->processMetadata(m_datasetObject);
0390 }
0391 
0392 /**
0393  * @brief Returns the QJsonObject associated with the currently selected dataset.
0394  */
0395 QJsonObject ImportDatasetWidget::loadDatasetObject() {
0396     for (const QJsonValueRef col : m_collections) {
0397         const QJsonObject& collectionJson = col.toObject();
0398         const QString& collection = collectionJson[QLatin1String("name")].toString();
0399 
0400         //we have to find the selected collection in the metadata file.
0401         if (m_allCollections || m_collection == collection) {
0402             QFile file(m_jsonDir + QLatin1Char('/') + collection + QLatin1String(".json"));
0403 
0404             //open the metadata file of the current collection
0405             if (file.open(QIODevice::ReadOnly)) {
0406                 QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
0407                 file.close();
0408                 if (!doc.isObject()) {
0409                     DEBUG("The " <<  STDSTRING(collection) << ".json file is invalid");
0410                     return QJsonObject();
0411                 }
0412 
0413                 QJsonArray categoryArray = doc.object().value(QLatin1String("categories")).toArray();
0414 
0415                 //processing categories
0416                 for (const QJsonValueRef cat : categoryArray) {
0417                     const QJsonObject currentCategory = cat.toObject();
0418                     const QString categoryName = currentCategory.value(QLatin1String("name")).toString();
0419                     if (m_category.isEmpty() || categoryName.compare(m_category) == 0) {
0420                         const QJsonArray subcategories = currentCategory.value(QLatin1String("subcategories")).toArray();
0421 
0422                         //processing subcategories
0423                         for (const auto& sub : subcategories) {
0424                             QJsonObject currentSubCategory = sub.toObject();
0425                             QString subcategoryName = currentSubCategory.value(QLatin1String("name")).toString();
0426 
0427                             if (m_subcategory.isEmpty() || subcategoryName.compare(m_subcategory) == 0) {
0428                                 const QJsonArray datasetArray = currentSubCategory.value(QLatin1String("datasets")).toArray();
0429 
0430                                 //processing the datasets of the actual subcategory
0431                                 for (const auto& dataset : datasetArray) {
0432                                     if (getSelectedDataset().compare(dataset.toObject().value(QLatin1String("filename")).toString()) == 0)
0433                                         return dataset.toObject();
0434                                 }
0435 
0436                                 if (!m_subcategory.isEmpty())
0437                                     break;
0438                             }
0439                         }
0440 
0441                         if (!m_category.isEmpty())
0442                             break;
0443                     }
0444                 }
0445             }
0446 
0447             if (!m_allCollections)
0448                 break;
0449         }
0450     }
0451 
0452     return QJsonObject();
0453 }
0454 
0455 /**
0456  * @brief Returns the structure containing the categories, subcategories and datasets.
0457  * @return the structure containing the categories, subcategories and datasets
0458  */
0459 const DatasetsMap& ImportDatasetWidget::getDatasetsMap() {
0460     return m_datasetsMap;
0461 }
0462 
0463 /**
0464  * @brief Sets the currently selected collection
0465  * @param category the name of the collection
0466  */
0467 void ImportDatasetWidget::setCollection(const QString& collection) {
0468     ui.cbCollections->setCurrentText(collection + QLatin1String(" (")
0469                 + QString(m_model->datasetCount(collection)) + QLatin1Char(')'));
0470 }
0471 
0472 /**
0473  * @brief Sets the currently selected category
0474  * @param category the name of the category
0475  */
0476 void ImportDatasetWidget::setCategory(const QString &category) {
0477     for (int i = 0; i < ui.twCategories->topLevelItemCount(); i++) {
0478         if (ui.twCategories->topLevelItem(i)->text(0).compare(category) == 0) {
0479             updateDatasets(ui.twCategories->topLevelItem(i));
0480             break;
0481         }
0482     }
0483 }
0484 
0485 /**
0486  * @brief Sets the currently selected subcategory
0487  * @param subcategory the name of the subcategory
0488  */
0489 void ImportDatasetWidget::setSubcategory(const QString &subcategory) {
0490     for (int i = 0; i < ui.twCategories->topLevelItemCount(); i++) {
0491         if (ui.twCategories->topLevelItem(i)->text(0).compare(m_category) == 0) {
0492             QTreeWidgetItem* categoryItem = ui.twCategories->topLevelItem(i);
0493             for (int j = 0; j <categoryItem->childCount(); j++) {
0494                 if (categoryItem->child(j)->text(0).compare(subcategory) == 0) {
0495                     updateDatasets(categoryItem->child(j));
0496                     break;
0497                 }
0498             }
0499             break;
0500         }
0501     }
0502 }
0503 
0504 /**
0505  * @brief  Sets the currently selected dataset
0506  * @param the currently selected dataset
0507  */
0508 void ImportDatasetWidget::setDataset(const QString &datasetName) {
0509     for (int i = 0; i < ui.lwDatasets->count() ; i++) {
0510         if (ui.lwDatasets->item(i)->text().compare(datasetName) == 0) {
0511             ui.lwDatasets->item(i)->setSelected(true);
0512             break;
0513         }
0514     }
0515 }
0516 
0517 /**
0518  * @brief Updates the details of the currently selected dataset
0519  */
0520 void ImportDatasetWidget::datasetChanged() {
0521     QString dataset = getSelectedDataset();
0522 
0523     //no need to fetch the same dataset description again if it's already shown
0524     if (m_collection == m_prevCollection && m_category == m_prevCategory
0525         && m_subcategory == m_prevSubcategory && dataset == m_prevDataset)
0526         return;
0527 
0528     m_prevCollection = m_collection;
0529     m_prevCategory = m_category;
0530     m_prevSubcategory = m_subcategory;
0531     m_prevDataset = dataset;
0532 
0533     QString info;
0534     if (ui.cbCollections->currentIndex() != 0) {
0535         const QString& m_collection = ui.cbCollections->itemData(ui.cbCollections->currentIndex()).toString();
0536         for (const QJsonValueRef col : m_collections) {
0537             const QJsonObject& collection = col.toObject();
0538             if ( m_collection.startsWith(collection[QLatin1String("name")].toString()) ) {
0539                 info += collection[QLatin1String("description")].toString();
0540                 info += QLatin1String("<br><br>");
0541                 break;
0542             }
0543         }
0544     }
0545 
0546     if (!dataset.isEmpty()) {
0547         m_datasetObject = loadDatasetObject();
0548 
0549         info += QLatin1String("<b>") + i18n("Dataset") + QLatin1String(":</b><br>");
0550         info += m_datasetObject[QLatin1String("name")].toString();
0551         info += QLatin1String("<br><br>");
0552 
0553         if (m_datasetObject.contains(QLatin1String("description_url"))) {
0554             WAIT_CURSOR;
0555             if (m_networkManager->networkAccessible() == QNetworkAccessManager::Accessible)
0556                 m_networkManager->get(QNetworkRequest(QUrl(m_datasetObject[QLatin1String("description_url")].toString())));
0557             else
0558                 info += m_datasetObject[QLatin1String("description")].toString();
0559         } else {
0560             info += QLatin1String("<b>") + i18n("Description") + QLatin1String(":</b><br>");
0561             info += m_datasetObject[QLatin1String("description")].toString();
0562         }
0563     } else
0564         m_datasetObject = QJsonObject();
0565 
0566     ui.lInfo->setText(info);
0567     emit datasetSelected();
0568 }
0569 
0570 void ImportDatasetWidget::downloadFinished(QNetworkReply* reply) {
0571     if (reply->error() == QNetworkReply::NoError) {
0572         QByteArray ba = reply->readAll();
0573         QString info(ba);
0574 
0575         if (m_collection == QLatin1String("Rdatasets")) {
0576             //detailed descriptions for R is in html format,
0577             //remove the header from the html file since we construct our own header
0578 
0579             int headerStart = info.indexOf(QLatin1String("<head>"));
0580             int headerEnd = info.indexOf(QLatin1String("</head>"));
0581             info = info.left(headerStart) + info.right(info.length() - headerEnd - 7);
0582 
0583             headerStart = info.indexOf(QLatin1String("<table"));
0584             headerEnd = info.indexOf(QLatin1String("</table>"));
0585             info = info.left(headerStart) + info.right(info.length() - headerEnd - 8);
0586 
0587             headerStart = info.indexOf(QLatin1String("<h2>"));
0588             headerEnd = info.indexOf(QLatin1String("</h2>"));
0589             info = info.left(headerStart) + info.right(info.length() - headerEnd - 5);
0590 
0591             info = info.replace(QLatin1String("<body>\n\n\n\n\n\n"), QLatin1String("<body>"));
0592             info = info.remove(QLatin1String("\n\n\n"));
0593         } else
0594             info = info.replace(QLatin1Char('\n'), QLatin1String("<br>"));
0595 
0596         //do further collection specific replacements to get better formatting
0597         if (m_collection == QLatin1String("JSEDataArchive")) {
0598             info = info.replace(QLatin1String("NAME:"), QLatin1String("<b>NAME:</b>"), Qt::CaseInsensitive);
0599             info = info.replace(QLatin1String("TYPE:"), QLatin1String("<b>TYPE:</b>"), Qt::CaseSensitive);
0600             info = info.replace(QLatin1String("SIZE:"), QLatin1String("<b>SIZE:</b>"), Qt::CaseSensitive);
0601             info = info.replace(QLatin1String("DESCRIPTIVE ABSTRACT:"), QLatin1String("<b>DESCRIPTIVE ABSTRACT:</b>"), Qt::CaseInsensitive);
0602             info = info.replace(QLatin1String("NOTE:"), QLatin1String("<b>NOTE:</b>"), Qt::CaseSensitive);
0603             info = info.replace(QLatin1String("SPECIAL NOTES:"), QLatin1String("<b>SPECIAL NOTES:</b>"), Qt::CaseSensitive);
0604             info = info.replace(QLatin1String("SOURCE:"), QLatin1String("<b>SOURCE:</b>"), Qt::CaseSensitive);
0605             info = info.replace(QLatin1String("SOURCES:"), QLatin1String("<b>SOURCES:</b>"), Qt::CaseInsensitive);
0606             info = info.replace(QLatin1String("DATA <b>SOURCE:</b>"), QLatin1String("<b>DATA SOURCE:</b>"), Qt::CaseSensitive);
0607             info = info.replace(QLatin1String("DATASET LAYOUT:"), QLatin1String("<b>DATASET LAYOUT:</b>"), Qt::CaseSensitive);
0608             info = info.replace(QLatin1String("DATASETS LAYOUT:"), QLatin1String("<b>DATASETS LAYOUT:</b>"), Qt::CaseSensitive);
0609             info = info.replace(QLatin1String("VARIABLE DESCRIPTIONS:"), QLatin1String("<b>VARIABLE DESCRIPTIONS:</b>"), Qt::CaseSensitive);
0610             info = info.replace(QLatin1String("VARIABLES DESCRIPTIONS:"), QLatin1String("<b>VARIABLES DESCRIPTIONS:</b>"), Qt::CaseSensitive);
0611             info = info.replace(QLatin1String("RELATED DATASETS:"), QLatin1String("<b>RELATED DATASETS:</b>"), Qt::CaseSensitive);
0612             info = info.replace(QLatin1String("SPECIAL NOTES:"), QLatin1String("<b>SPECIAL NOTES:</b>"), Qt::CaseSensitive);
0613             info = info.replace(QLatin1String("STORY BEHIND THE DATA:"), QLatin1String("<b>STORY BEHIND THE DATA:</b>"), Qt::CaseSensitive);
0614             info = info.replace(QLatin1String("THE <b>STORY BEHIND THE DATA:</b>"), QLatin1String("<b>THE STORY BEHIND THE DATA:</b>"), Qt::CaseSensitive);
0615             info = info.replace(QLatin1String("PEDAGOGICAL NOTES:"), QLatin1String("<b>PEDAGOGICAL NOTES:</b>"), Qt::CaseSensitive);
0616             info = info.replace(QLatin1String("REFERENCE:"), QLatin1String("<b>REFERENCE:</b>"), Qt::CaseSensitive);
0617             info = info.replace(QLatin1String("REFERENCES:"), QLatin1String("<b>REFERENCES:</b>"), Qt::CaseSensitive);
0618             info = info.replace(QLatin1String("SUBMITTED BY:"), QLatin1String("<b>SUBMITTED BY:</b>"), Qt::CaseSensitive);
0619         }
0620         ui.lInfo->setText(ui.lInfo->text() + info);
0621     } else {
0622         DEBUG("Failed to fetch the description.");
0623         ui.lInfo->setText(ui.lInfo->text() + m_datasetObject[QLatin1String("description")].toString());
0624     }
0625     reply->deleteLater();
0626     RESET_CURSOR;
0627 }