File indexing completed on 2025-07-13 03:32:34

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