diff --git a/src/kdefrontend/datasources/DatasetMetadataManagerWidget.cpp b/src/kdefrontend/datasources/DatasetMetadataManagerWidget.cpp index d540c58d4..5edb54fcf 100644 --- a/src/kdefrontend/datasources/DatasetMetadataManagerWidget.cpp +++ b/src/kdefrontend/datasources/DatasetMetadataManagerWidget.cpp @@ -1,616 +1,625 @@ /*************************************************************************** File : DatasetMetadataManagerWidget.cpp Project : LabPlot Description : widget for managing a metadata file of a dataset -------------------------------------------------------------------- Copyright : (C) 2019 Ferencz Kovacs (kferike98@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "backend/datasources/filters/AsciiFilter.h" #include "src/kdefrontend/DatasetModel.h" #include "src/kdefrontend/datasources/DatasetMetadataManagerWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*! \class DatasetMetadataManagerWidget \brief Widget for adding a new dataset to LabPlot's current collection. \ingroup kdefrontend */ DatasetMetadataManagerWidget::DatasetMetadataManagerWidget(QWidget* parent, const QMap< QString, QMap>>>& datasetMap) : QWidget(parent) { ui.setupUi(this); m_datasetModel = new DatasetModel(datasetMap); m_baseColor = (palette().color(QPalette::Base).lightness() < 128) ? QLatin1String("#5f5f5f") : QLatin1String("#ffffff"); m_textColor = (palette().color(QPalette::Base).lightness() < 128) ? QLatin1String("#ffffff") : QLatin1String("#000000"); ui.cbCollection->addItems(m_datasetModel->collections()); ui.cbCategory->addItems(m_datasetModel->categories(ui.cbCollection->currentText())); ui.cbSubcategory->addItems(m_datasetModel->subcategories(ui.cbCollection->currentText(), ui.cbCategory->currentText())); ui.cbSeparatingCharacter->addItems(AsciiFilter::separatorCharacters()); ui.cbCommentCharacter->addItems(AsciiFilter::commentCharacters()); ui.cbNumberFormat->addItems(AbstractFileFilter::numberFormats()); ui.cbDateTimeFormat->addItems(AbstractColumn::dateTimeFormats()); connect(ui.leDatasetName, &QLineEdit::textChanged, [this] { emit checkOk(); }); connect(ui.leDownloadURL, &QLineEdit::textChanged, [this] { emit checkOk(); }); connect(ui.teDescription, &QTextEdit::textChanged, [this] { emit checkOk(); }); connect(ui.leFileName, &QLineEdit::textChanged, [this] { emit checkOk(); }); connect(ui.cbSubcategory, &QComboBox::currentTextChanged, [this] { emit checkOk(); }); connect(ui.cbCollection, &QComboBox::currentTextChanged, this, &DatasetMetadataManagerWidget::updateCategories); connect(ui.cbCategory, &QComboBox::currentTextChanged, this, &DatasetMetadataManagerWidget::updateSubcategories); connect(ui.bNewColumn, &QPushButton::clicked, this, &DatasetMetadataManagerWidget::addColumnDescription); connect(ui.bDelete, &QPushButton::clicked, this, &DatasetMetadataManagerWidget::removeColumnDescription); loadSettings(); } DatasetMetadataManagerWidget::~DatasetMetadataManagerWidget() { KConfigGroup conf(KSharedConfig::openConfig(), "DatasetMetadataManagerWidget"); //filter settings conf.writeEntry("separator", ui.cbSeparatingCharacter->currentText()); conf.writeEntry("commentChar", ui.cbCommentCharacter->currentText()); conf.writeEntry("numberFormat", ui.cbNumberFormat->currentIndex()); conf.writeEntry("dateTimeFormat", ui.cbDateTimeFormat->currentText()); conf.writeEntry("createIndexColumn", ui.chbCreateIndex->isChecked()); conf.writeEntry("skipEmptyParts", ui.chbSkipEmptyParts->isChecked()); conf.writeEntry("simplifyWhitespaces", ui.chbSimplifyWhitespaces->isChecked()); conf.writeEntry("removeQuotes", ui.chbRemoveQuotes->isChecked()); conf.writeEntry("useFirstRowForVectorName", ui.chbHeader->isChecked()); conf.writeEntry("collection", ui.cbCollection->currentText()); conf.writeEntry("category", ui.cbCategory->currentText()); conf.writeEntry("subcategory", ui.cbSubcategory->currentText()); } /** * @brief Loads the settings of the widget. */ void DatasetMetadataManagerWidget::loadSettings() { KConfigGroup conf(KSharedConfig::openConfig(), "DatasetMetadataManagerWidget"); ui.cbCommentCharacter->setCurrentItem(conf.readEntry("commentChar", "#")); ui.cbSeparatingCharacter->setCurrentItem(conf.readEntry("separator", "auto")); ui.cbNumberFormat->setCurrentIndex(conf.readEntry("numberFormat", static_cast(QLocale::AnyLanguage))); ui.cbDateTimeFormat->setCurrentItem(conf.readEntry("dateTimeFormat", "yyyy-MM-dd hh:mm:ss.zzz")); ui.chbCreateIndex->setChecked(conf.readEntry("createIndexColumn", false)); ui.chbSimplifyWhitespaces->setChecked(conf.readEntry("simplifyWhitespaces", true)); ui.chbRemoveQuotes->setChecked(conf.readEntry("removeQuotes", false)); ui.chbSkipEmptyParts->setChecked(conf.readEntry("skipEmptyParts", false)); ui.chbHeader->setChecked(conf.readEntry("useFirstRowForVectorName", true)); - ui.cbCollection->setCurrentText(conf.readEntry("collection", "")); - ui.cbCategory->setCurrentText(conf.readEntry("category", "")); - ui.cbSubcategory->setCurrentText(conf.readEntry("subcategory", "")); + + QString lastCollection = conf.readEntry("collection", ""); + if(m_datasetModel->collections().contains(lastCollection)) + ui.cbCollection->setCurrentText(lastCollection); + + QString lastCategory = conf.readEntry("category", ""); + if(m_datasetModel->categories(ui.cbCollection->currentText()).contains(lastCategory)) + ui.cbCategory->setCurrentText(lastCategory); + + QString lastSubcategory = conf.readEntry("subcategory", ""); + if(m_datasetModel->subcategories(ui.cbCollection->currentText(), ui.cbCategory->currentText()).contains(lastSubcategory)) + ui.cbSubcategory->setCurrentText(lastSubcategory); } /** * @brief Checks whether leFileName contains a valid file name. */ bool DatasetMetadataManagerWidget::checkFileName() { const QString fileName = ui.leFileName->text(); //check whether it contains only digits, letters, -, _ or not const QRegularExpression re("^[\\w\\d-]+$"); const QRegularExpressionMatch match = re.match(fileName); bool hasMatch = match.hasMatch(); if(!hasMatch || fileName.isEmpty()) { qDebug("File name invalid"); QPalette palette; palette.setColor(QPalette::Base, Qt::red); palette.setColor(QPalette::Text, Qt::black); ui.leFileName->setPalette(palette); ui.leFileName->setToolTip("Invalid name for a file (it can contain:digits, letters, -, _)"); } else { QPalette palette; palette.setColor(QPalette::Base, m_baseColor); palette.setColor(QPalette::Text, m_textColor); ui.leFileName->setPalette(palette); ui.leFileName->setToolTip(""); } //check whether there already is a file named like this or not. bool found = false; if(m_datasetModel->allDatasetsList().toStringList().contains(fileName)) { qDebug("There already is a metadata file with this name"); QPalette palette; palette.setColor(QPalette::Base, Qt::red); palette.setColor(QPalette::Text, Qt::black); ui.leFileName->setPalette(palette); ui.leFileName->setToolTip("There already is a dataset metadata file with this name!"); found = true; } else { if(hasMatch) { QPalette palette; palette.setColor(QPalette::Base, m_baseColor); palette.setColor(QPalette::Text, m_textColor); ui.leFileName->setPalette(palette); ui.leFileName->setToolTip(""); } } return hasMatch && !found; } /** * @brief Checks whether leDownloadURL contains a valid URL. */ bool DatasetMetadataManagerWidget::urlExists() { //Check whether the given url is acceptable syntactically const QRegularExpression re("^(?:http(s)?:\\/\\/)?[\\w.-]+(?:\\.[\\w\\.-]+)+[\\w\\-\\._~:/?#[\\]@!\\$&'\\(\\)\\*\\+,;=.]+$"); const QRegularExpressionMatch match = re.match(ui.leDownloadURL->text()); bool hasMatch = match.hasMatch(); const bool urlExists = hasMatch && !ui.leDownloadURL->text().isEmpty(); if(!urlExists){ QPalette palette; palette.setColor(QPalette::Base, Qt::red); palette.setColor(QPalette::Text, Qt::black); ui.leDownloadURL->setPalette(palette); ui.leDownloadURL->setToolTip("The URL is invalid!"); } else { QPalette palette; palette.setColor(QPalette::Base, m_baseColor); palette.setColor(QPalette::Text, m_textColor);; ui.leDownloadURL->setPalette(palette); ui.leDownloadURL->setToolTip(""); } return urlExists; } /** * @brief Checks whether leDatasetName is empty or not. */ bool DatasetMetadataManagerWidget::checkDatasetName() { const bool longNameOk = !ui.leDatasetName->text().isEmpty(); if(!longNameOk) { QPalette palette; palette.setColor(QPalette::Base, Qt::red); palette.setColor(QPalette::Text, Qt::black); ui.leDatasetName->setPalette(palette); ui.leDatasetName->setToolTip("Please fill this out!"); } else { QPalette palette; palette.setColor(QPalette::Base, m_baseColor); palette.setColor(QPalette::Text, m_textColor); ui.leDatasetName->setPalette(palette); ui.leDatasetName->setToolTip(""); } return longNameOk; } /** * @brief Checks whether teDescription is empty or not. */ bool DatasetMetadataManagerWidget::checkDescription() { const bool descriptionOk = !ui.teDescription->toPlainText().isEmpty(); if(!descriptionOk) { QPalette palette; palette.setColor(QPalette::Base, Qt::red); palette.setColor(QPalette::Text, Qt::black); ui.teDescription->setPalette(palette); ui.teDescription->setToolTip("Please fill this out!"); } else { QPalette palette; palette.setColor(QPalette::Base, m_baseColor); palette.setColor(QPalette::Text, m_textColor); ui.teDescription->setPalette(palette); ui.teDescription->setToolTip(""); } return descriptionOk; } /** * @brief Checks whether the given QComboBox's current text is empty or not. */ bool DatasetMetadataManagerWidget::checkCategories(QComboBox* comboBox) { //Check whether it is a word or not (might contain digits) const QString fileName = comboBox->currentText(); const QRegularExpression re("^[\\w\\d]+$"); const QRegularExpressionMatch match = re.match(fileName); const bool hasMatch = match.hasMatch(); if(!hasMatch || fileName.isEmpty()) { qDebug("category/subcategory name invalid"); QPalette palette; palette.setColor(QPalette::Base,Qt::red); palette.setColor(QPalette::Text,Qt::black); comboBox->setPalette(palette); comboBox->setToolTip("Invalid or empty name for a category/subcategory (only digits and letters)"); } else { QPalette palette; palette.setColor(QPalette::Base, m_baseColor); palette.setColor(QPalette::Text, m_textColor); comboBox->setPalette(palette); comboBox->setToolTip(""); } return hasMatch; } /** * @brief Enables/disables the widget's components meant to configure the metadata file of the new dataset. */ void DatasetMetadataManagerWidget::enableDatasetSettings(bool enable) { ui.leFileName->setEnabled(enable); ui.leFileName->setReadOnly(!enable); ui.leDatasetName->setEnabled(enable); ui.leDatasetName->setReadOnly(!enable); ui.leDownloadURL->setEnabled(enable); ui.leDownloadURL->setReadOnly(!enable); ui.teDescription->setEnabled(enable); ui.teDescription->setReadOnly(!enable); ui.gbColumnDescriptions->setEnabled(enable); ui.gbFilter->setEnabled(enable); } /** * @brief Checks whether the introduced data is valid or not. Used by DatasetMetadataManagerDialog. */ bool DatasetMetadataManagerWidget::checkDataValidity() { const bool fileNameOK = checkFileName(); const bool urlOk = urlExists(); const bool longNameOk = checkDatasetName(); const bool descriptionOk = checkDescription(); const bool categoryOk = checkCategories(ui.cbCategory); const bool subcategoryOk = checkCategories(ui.cbSubcategory); const bool collectionOk = checkCategories(ui.cbCollection); enableDatasetSettings(categoryOk && subcategoryOk && collectionOk); return fileNameOK && urlOk && longNameOk && descriptionOk && subcategoryOk && categoryOk && collectionOk; } /** * @brief Updates content of cbCategory based on current collection. */ void DatasetMetadataManagerWidget::updateCategories(const QString& collection) { ui.cbCategory->clear(); if( m_datasetModel->collections().contains(collection)) ui.cbCategory->addItems(m_datasetModel->categories(collection)); emit checkOk(); } /** * @brief Updates content of cbSubcategory based on current category. */ void DatasetMetadataManagerWidget::updateSubcategories(const QString& category) { ui.cbSubcategory->clear(); const QString collection = ui.cbCollection->currentText(); if( m_datasetModel->categories(collection).contains(category)) ui.cbSubcategory->addItems(m_datasetModel->subcategories(collection, category)); emit checkOk(); } /** * @brief Updates the metadata file containing the categories, subcategories and datasets. * @param fileName the name of the metadata file (path) */ void DatasetMetadataManagerWidget::updateDocument(const QString& dirPath) { if(checkDataValidity()) { //Check whether the current collection already exists, if yes update it if(m_datasetModel->collections().contains(ui.cbCollection->currentText())) { QString fileName = dirPath + ui.cbCollection->currentText() + ".json"; qDebug() << "Updating: " << fileName; QFile file(fileName); if (file.open(QIODevice::ReadWrite)) { QJsonDocument document = QJsonDocument::fromJson(file.readAll()); QJsonObject rootObject = document.object(); QJsonValueRef categoryArrayRef = rootObject.find("categories").value(); QJsonArray categoryArray = categoryArrayRef.toArray(); //Check whether the current category already exists bool foundCategory = false; for(int i = 0 ; i < categoryArray.size(); ++i) { QJsonValueRef categoryRef = categoryArray[i]; QJsonObject currentCategory = categoryRef.toObject(); QString categoryName = currentCategory.value("category_name").toString(); //If we find the category we have to update that QJsonObject if(categoryName.compare(ui.cbCategory->currentText()) == 0) { foundCategory = true; QJsonValueRef subcategoryArrayRef = currentCategory.find("subcategories").value(); QJsonArray subcategoryArray = subcategoryArrayRef.toArray(); //Check whether the current subcategory already exists bool subcategoryFound = false; for(int j = 0; j < subcategoryArray.size(); ++j) { QJsonValueRef subcategoryRef = subcategoryArray[j]; QJsonObject currentSubcategory = subcategoryRef.toObject(); QString subcategoryName = currentSubcategory.value("subcategory_name").toString(); //If we find the subcategory we have to update that QJsonObject if(subcategoryName.compare(ui.cbSubcategory->currentText()) == 0) { subcategoryFound = true; QJsonValueRef datasetsRef = currentSubcategory.find("datasets").value(); QJsonArray datasets = datasetsRef.toArray(); datasets.append(createDatasetObject()); datasetsRef = datasets; subcategoryRef = currentSubcategory; subcategoryArrayRef = subcategoryArray; categoryRef = currentCategory; categoryArrayRef = categoryArray; document.setObject(rootObject); break; } } //If we didn't find the subcategory, we have to create it if(!subcategoryFound) { qDebug() << "Subcategory not found"; QJsonObject newSubcategory; newSubcategory.insert("subcategory_name", ui.cbSubcategory->currentText()); QJsonArray datasets; datasets.append(createDatasetObject()); newSubcategory.insert("datasets", datasets); subcategoryArray.append(newSubcategory); subcategoryArrayRef = subcategoryArray; categoryRef = currentCategory; categoryArrayRef = categoryArray; document.setObject(rootObject); } break; } } //If we didn't find the category, we have to create it if(!foundCategory) { qDebug() << "Category not found"; QJsonObject newCategory; newCategory.insert("category_name", ui.cbCategory->currentText()); QJsonArray subcategoryArray; QJsonObject newSubcategory; newSubcategory.insert("subcategory_name", ui.cbSubcategory->currentText()); QJsonArray datasets; datasets.append(createDatasetObject()); newSubcategory.insert("datasets", datasets); subcategoryArray.append(newSubcategory); newCategory.insert("subcategories", subcategoryArray); categoryArray.append(newCategory); categoryArrayRef = categoryArray; document.setObject(rootObject); } file.close(); file.open(QIODevice::ReadWrite | QIODevice::Truncate); file.write(document.toJson()); file.close(); } else { qDebug() << "Couldn't open dataset category file, because " << file.errorString(); } } //If the collection doesn't exist we have to create a new json file for it. else { QString fileName = dirPath + "DatasetCollections.json"; qDebug() << "creating: " << fileName; QFile file(fileName); if (file.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) { QJsonArray collectionArray; for(QString collection : m_datasetModel->collections()) collectionArray.append(collection); collectionArray.append(ui.cbCollection->currentText()); QJsonDocument newDocument; newDocument.setArray(collectionArray); file.write(newDocument.toJson()); file.close(); } QJsonObject rootObject; rootObject.insert("collection_name", ui.cbCollection->currentText()); QJsonArray categoryArray; QJsonObject newCategory; newCategory.insert("category_name", ui.cbCategory->currentText()); QJsonArray subcategoryArray; QJsonObject newSubcategory; newSubcategory.insert("subcategory_name", ui.cbSubcategory->currentText()); QJsonArray datasets; datasets.append(createDatasetObject()); newSubcategory.insert("datasets", datasets); subcategoryArray.append(newSubcategory); newCategory.insert("subcategories", subcategoryArray); categoryArray.append(newCategory); rootObject.insert("categories", categoryArray); QJsonDocument document; document.setObject(rootObject); QFile collectionFile(dirPath + ui.cbCollection->currentText() + ".json"); if (collectionFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { collectionFile.write(document.toJson()); collectionFile.close(); } } } } /** * @brief Creates and returns a QJsonObject based on the given settings of the widget, this will be part of the collection's metadata file */ QJsonObject DatasetMetadataManagerWidget::createDatasetObject() { QJsonObject rootObject; rootObject.insert("filename", ui.leFileName->text()); rootObject.insert("name", ui.leDatasetName->text()); rootObject.insert("download", ui.leDownloadURL->text()); rootObject.insert("description", ui.teDescription->toPlainText()); rootObject.insert("separator", ui.cbSeparatingCharacter->currentText()); rootObject.insert("comment_character", ui.cbCommentCharacter->currentText()); rootObject.insert("DateTime_format", ui.cbDateTimeFormat->currentText()); rootObject.insert("number_format", ui.cbNumberFormat->currentIndex()); rootObject.insert("create_index_column", ui.chbCreateIndex->isChecked()); rootObject.insert("skip_empty_parts", ui.chbSkipEmptyParts->isChecked()); rootObject.insert("simplify_whitespaces", ui.chbSimplifyWhitespaces->isChecked()); rootObject.insert("remove_quotes", ui.chbRemoveQuotes->isChecked()); rootObject.insert("use_first_row_for_vectorname", ui.chbHeader->isChecked()); for(int i = 0; i < m_columnDescriptions.size(); ++i) rootObject.insert(i18n("column_description_%1", i), m_columnDescriptions[i]); return rootObject; } /** * @brief Adds a new QLineEdit so the user can set a new column description. */ void DatasetMetadataManagerWidget::addColumnDescription() { QLabel* label = new QLabel(); label->setText(i18n("Description for column %1", m_columnDescriptions.size() + 1)); QLineEdit* lineEdit = new QLineEdit; int layoutIndex = m_columnDescriptions.size() + 1; ui.columnLayout->addWidget(label, layoutIndex, 0); ui.columnLayout->addWidget(lineEdit, layoutIndex, 1, 1, -1); connect(lineEdit, &QLineEdit::textChanged, [this, layoutIndex] (const QString& text) { m_columnDescriptions[layoutIndex - 1] = text; }); m_columnDescriptions.append(""); } /** * @brief Removes the lastly added QLineEdit (used to set a column description). */ void DatasetMetadataManagerWidget::removeColumnDescription() { const int index = ui.columnLayout->count() - 1; QLayoutItem *item; if ((item = ui.columnLayout->takeAt(index)) != nullptr) { delete item->widget(); delete item; } if ((item = ui.columnLayout->takeAt(index - 1)) != nullptr){ delete item->widget(); delete item; } m_columnDescriptions.removeLast(); } /** * @brief returns the path to the new metadata file of the new dataset. */ QString DatasetMetadataManagerWidget::getMetadataFilePath() const { return m_metadataFilePath; } /** * @brief Sets the collection name. */ void DatasetMetadataManagerWidget::setCollection(const QString& collection) { ui.cbCollection->setCurrentText(collection); } /** * @brief Sets the category name. */ void DatasetMetadataManagerWidget::setCategory(const QString& category) { ui.cbCategory->setCurrentText(category); } /** * @brief Sets the subcategory name. */ void DatasetMetadataManagerWidget::setSubcategory(const QString& subcategory) { ui.cbSubcategory->setCurrentText(subcategory); } /** * @brief Sets the short name of the dataset. */ void DatasetMetadataManagerWidget::setShortName(const QString& name) { ui.leFileName->setText(name); } /** * @brief Sets the full name of the dataset. */ void DatasetMetadataManagerWidget::setFullName(const QString& name) { ui.leDatasetName->setText(name); } /** * @brief Sets the text of the description. */ void DatasetMetadataManagerWidget::setDescription(const QString& description) { ui.teDescription->setText(description); } /** * @brief Sets the download url. */ void DatasetMetadataManagerWidget::setURL(const QString& url) { ui.leDownloadURL->setText(url); } diff --git a/src/kdefrontend/datasources/ImportDatasetWidget.cpp b/src/kdefrontend/datasources/ImportDatasetWidget.cpp index a4ab3a2db..af55ac56e 100644 --- a/src/kdefrontend/datasources/ImportDatasetWidget.cpp +++ b/src/kdefrontend/datasources/ImportDatasetWidget.cpp @@ -1,828 +1,897 @@ /*************************************************************************** File : ImportDatasetWidget.cpp Project : LabPlot Description : import online dataset widget -------------------------------------------------------------------- Copyright : (C) 2019 Kovacs Ferencz (kferike98@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "src/backend/datasources/DatasetHandler.h" #include "src/kdefrontend/datasources/ImportDatasetWidget.h" #include "src/kdefrontend/datasources/DatasetMetadataManagerDialog.h" #include "src/kdefrontend/DatasetModel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*! \class ImportDatasetWidget \brief Widget for importing data from a dataset. \ingroup kdefrontend */ ImportDatasetWidget::ImportDatasetWidget(QWidget* parent) : QWidget(parent), m_categoryCompleter(new QCompleter), m_datasetCompleter(new QCompleter), m_loadingCategories(false) { const QString baseDir = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation).first(); QString containingDir = "labplot_data"; m_jsonDir = baseDir + QDir::separator() + containingDir + QDir::separator(); ui.setupUi(this); if(!QFile(m_jsonDir + "DatasetCollections.json").exists()) downloadCollectionsFile(); loadDatasetCategoriesFromJson(); ui.lwDatasets->setSelectionMode(QAbstractItemView::SingleSelection); ui.twCategories->setSelectionMode(QAbstractItemView::SingleSelection); ui.lDescription->setWordWrap(true); ui.lFullName->setWordWrap(true); showDetails(m_showDetails); connect(ui.cbCollections, &QComboBox::currentTextChanged, this, &ImportDatasetWidget::updateCategoryTree); connect(ui.twCategories, &QTreeWidget::itemDoubleClicked, this, &ImportDatasetWidget::listDatasetsForSubcategory); connect(ui.twCategories, &QTreeWidget::itemSelectionChanged, [this] { if(!m_loadingCategories) listDatasetsForSubcategory(ui.twCategories->selectedItems().first()); }); connect(ui.leSearchDatasets, &QLineEdit::textChanged, this, &ImportDatasetWidget::scrollToDatasetListItem); connect(ui.bClearCache, &QPushButton::clicked, this, &ImportDatasetWidget::clearCache); connect(ui.leSearchCategories, &QLineEdit::textChanged, this, &ImportDatasetWidget::scrollToCategoryTreeItem); connect(ui.bRefresh, &QPushButton::clicked, this, &ImportDatasetWidget::refreshCategories); + connect(ui.bRestore, &QPushButton::clicked, this, &ImportDatasetWidget::restoreBackup); connect(ui.bNewDataset, &QPushButton::clicked, this, &ImportDatasetWidget::showDatasetMetadataManager); connect(ui.lwDatasets, &QListWidget::itemSelectionChanged, [this]() { emit datasetSelected(); if(m_showDetails) updateDetails(); }); connect(ui.lwDatasets, &QListWidget::doubleClicked, [this]() { emit datasetDoubleClicked(); }); connect(ui.bShowDetails, &QPushButton::clicked, [this]() { m_showDetails = !m_showDetails; if(m_showDetails) { ui.bShowDetails->setText("Hide details"); } else { ui.bShowDetails->setText("Show details"); } showDetails(m_showDetails); }); } ImportDatasetWidget::~ImportDatasetWidget() { if(m_categoryCompleter != nullptr) delete m_categoryCompleter; if(m_datasetCompleter != nullptr) delete m_datasetCompleter; } /** * @brief Locates in the file system the json metadata file that contains the list of categories and subcategories. * @return The location of the file */ QString ImportDatasetWidget::locateCategoryJsonFile() const { qDebug() << "Locating category file" << QStandardPaths::locate(QStandardPaths::AppDataLocation, "datasets/DatasetCategories.json"); return QStandardPaths::locate(QStandardPaths::AppDataLocation, "datasets/DatasetCategories.json"); } /** * @brief Processes the json metadata file that contains the list of categories and subcategories and their datasets. */ void ImportDatasetWidget::loadDatasetCategoriesFromJson() { QString filePath = m_jsonDir + "DatasetCollections.json"; QFile file(filePath); if (file.open(QIODevice::ReadOnly)) { m_datasetsMap.clear(); ui.cbCollections->clear(); QJsonDocument document = QJsonDocument::fromJson(file.readAll()); QJsonArray collections; if(document.isArray()) collections = document.array(); else { qDebug()<< "The DatasetCollections.json file is invalid"; return; } for (int collectionIndex = 0; collectionIndex < collections.size(); collectionIndex++) { const QString currentCollection = collections[collectionIndex].toString(); if(!QFile(m_jsonDir + currentCollection + ".json").exists()) downloadCollectionFile(currentCollection + ".json"); QFile collectionFile(m_jsonDir + currentCollection + ".json"); if (collectionFile.open(QIODevice::ReadOnly)) { QJsonDocument collectionDocument = QJsonDocument::fromJson(collectionFile.readAll()); QJsonObject collectionObject; if(collectionDocument.isObject()) { collectionObject = collectionDocument.object(); } else { qDebug()<< "The " + currentCollection + ".json file is invalid"; return; } if(collectionObject.value("collection_name").toString().compare(currentCollection) != 0) { qDebug()<< "The " + currentCollection + ".json file name is invalid"; return; } QJsonArray categoryArray = collectionObject.value("categories").toArray(); //processing categories for(int i = 0 ; i < categoryArray.size(); ++i) { const QJsonObject currentCategory = categoryArray[i].toObject(); const QString categoryName = currentCategory.value("category_name").toString(); const QJsonArray subcategories = currentCategory.value("subcategories").toArray(); //processing subcategories for(int j = 0; j < subcategories.size(); ++j) { QJsonObject currentSubCategory = subcategories[j].toObject(); QString subcategoryName = currentSubCategory.value("subcategory_name").toString(); const QJsonArray datasetArray = currentSubCategory.value("datasets").toArray(); //processing the datasets o the actual subcategory for (const auto& dataset : datasetArray) { m_datasetsMap[currentCollection][categoryName][subcategoryName].push_back(dataset.toObject().value("filename").toString()); } } } } } if(m_datasetModel != nullptr) delete m_datasetModel; m_datasetModel = new DatasetModel(m_datasetsMap); //Fill up collections combo box ui.cbCollections->addItem(QString("All (" + QString::number(m_datasetModel->allDatasetsList().toStringList().size()) + ")")); for(QString collection : m_datasetModel->collections()) ui.cbCollections->addItem(collection + " (" + QString::number(m_datasetModel->datasetCount(collection)) + ")"); updateCategoryTree(ui.cbCollections->currentText()); restoreSelectedSubcategory(ui.cbCollections->currentText()); file.close(); } else { qDebug("Couldn't open dataset collections file"); } } /** * @brief Returns the valid collection name based on given collection name (containing the count of datasets of the given collection) */ QString ImportDatasetWidget::validCollectionName(const QString &collection) { int index = collection.lastIndexOf(" ("); QString collectionName = collection.left(index); return collectionName; } /** * @brief Updates/fills ui.twCategories based on the selected collection. */ void ImportDatasetWidget::updateCategoryTree(const QString& collectionName) { QString collection = validCollectionName(collectionName); m_loadingCategories = true; ui.lwDatasets->clear(); ui.twCategories->clear(); QStringList categories = (collection.compare("All") == 0) ? m_datasetModel->allCategories().toStringList() : m_datasetModel->categories(collection); //Go through every category that was previously processed. for(auto category : categories) { - QTreeWidgetItem* const currentCategoryItem = new QTreeWidgetItem(QStringList(category)); + QStringList categoryList(category); + categoryList.append(QString::number(m_datasetModel->datasetCount(collection, category))); + QTreeWidgetItem* const currentCategoryItem = new QTreeWidgetItem(categoryList); ui.twCategories->addTopLevelItem(currentCategoryItem); QStringList subcategories = (collection.compare("All") == 0) ? m_datasetModel->allSubcategories(category).toStringList() : m_datasetModel->subcategories(collection, category); //Go through every subcategory of the current category, that was previously processed. for(auto subcategory : subcategories) { - currentCategoryItem->addChild(new QTreeWidgetItem(QStringList(subcategory))); + QStringList subcategoryList(subcategory); + subcategoryList.append(QString::number(m_datasetModel->datasetCount(collection, category, subcategory))); + + currentCategoryItem->addChild(new QTreeWidgetItem(QStringList(subcategoryList))); } } if(m_selectedCollection.compare(collection) == 0) { restoreSelectedSubcategory(ui.cbCollections->currentText()); } else { m_selectedCollection = collection; m_selectedCategory = ""; m_selectedSubcategory = ""; } m_loadingCategories = false; updateCategoryCompleter(); } /** * @brief Restores the lastly selected collection, category and subcategory making it the selected QTreeWidgetItem and also lists the datasets belonigng to it */ void ImportDatasetWidget::restoreSelectedSubcategory(const QString& collectionName) { QString collection = validCollectionName(collectionName); ui.cbCollections->setCurrentText(collection); if(m_datasetModel->categories(collection).contains(m_selectedCategory)) { const QTreeWidgetItem* const categoryItem = ui.twCategories->findItems(m_selectedCategory, Qt::MatchExactly).first(); if(m_datasetModel->subcategories(collection, m_selectedCategory).contains(m_selectedSubcategory)) { for(int i = 0; i < categoryItem->childCount(); ++i) { if(categoryItem->child(i)->text(0).compare(m_selectedSubcategory) == 0) { QTreeWidgetItem* const subcategoryItem = categoryItem->child(i); ui.twCategories->setCurrentItem(subcategoryItem); subcategoryItem->setSelected(true); m_selectedSubcategory.clear(); listDatasetsForSubcategory(subcategoryItem); break; } } } } } /** * @brief Populates lwDatasets with the datasets of the selected subcategory. * @param item the selected subcategory */ void ImportDatasetWidget::listDatasetsForSubcategory(QTreeWidgetItem* item) { if(item->childCount() == 0) { if(m_selectedSubcategory.compare(item->text(0)) != 0) { m_selectedSubcategory = item->text(0); m_selectedCategory = item->parent()->text(0); QString categoryName = item->parent()->text(0); ui.lwDatasets->clear(); for(QString dataset : m_datasetModel->datasets(m_selectedCollection, categoryName, m_selectedSubcategory)) { ui.lwDatasets->addItem(new QListWidgetItem(dataset)); } updateDatasetCompleter(); highlightLocalMetadataFiles(); } } else { if(item->text(0).compare(m_selectedCategory) != 0) { m_selectedCategory = item->text(0); m_selectedSubcategory = ""; ui.lwDatasets->clear(); item->setExpanded(true); } } } /** * @brief Updates the completer used for searching among datasets. */ void ImportDatasetWidget::updateDatasetCompleter() { QStringList datasetList; for(int i = 0; i count(); ++i) { datasetList.append(ui.lwDatasets->item(i)->text()); } if(!datasetList.isEmpty()) { if(m_datasetCompleter != nullptr) delete m_datasetCompleter; m_datasetCompleter = new QCompleter(datasetList); m_datasetCompleter->setCompletionMode(QCompleter::PopupCompletion); m_datasetCompleter->setCaseSensitivity(Qt::CaseSensitive); ui.leSearchDatasets->setCompleter(m_datasetCompleter); } else ui.leSearchDatasets->setCompleter(nullptr); } /** * @brief Updates the completer used for searching among categories and subcategories. */ void ImportDatasetWidget::updateCategoryCompleter() { QStringList categoryList; for (int i = 0; i < ui.twCategories->topLevelItemCount(); ++i) { categoryList.append(ui.twCategories->topLevelItem(i)->text(0)); for(int j = 0; j < ui.twCategories->topLevelItem(i)->childCount(); ++j) { categoryList.append(ui.twCategories->topLevelItem(i)->text(0) + QLatin1Char(':') + ui.twCategories->topLevelItem(i)->child(j)->text(0)); } } if(!categoryList.isEmpty()) { if(m_categoryCompleter != nullptr) delete m_categoryCompleter; m_categoryCompleter = new QCompleter(categoryList); m_categoryCompleter->setCompletionMode(QCompleter::PopupCompletion); m_categoryCompleter->setCaseSensitivity(Qt::CaseSensitive); ui.leSearchCategories->setCompleter(m_categoryCompleter); } else ui.leSearchCategories->setCompleter(nullptr); } /** * @brief Scrolls the twCategories to the given category or subcategory * @param rootName the name of the category or category+subcategory */ void ImportDatasetWidget::scrollToCategoryTreeItem(const QString& rootName) { int topItemIdx = -1; for (int i = 0; i < ui.twCategories->topLevelItemCount(); ++i) if (rootName.startsWith(ui.twCategories->topLevelItem(i)->text(0))) { topItemIdx = i; break; } if (topItemIdx >= 0) { if(!rootName.contains(QLatin1Char(':'))) { ui.twCategories->scrollToItem(ui.twCategories->topLevelItem(topItemIdx), QAbstractItemView::ScrollHint::PositionAtTop); } else { int childIdx = -1; for(int j = 0; j < ui.twCategories->topLevelItem(topItemIdx)->childCount(); ++j) { if(rootName.endsWith(ui.twCategories->topLevelItem(topItemIdx)->child(j)->text(0))) { childIdx = j; break; } } if(childIdx >= 0) { ui.twCategories->scrollToItem(ui.twCategories->topLevelItem(topItemIdx)->child(childIdx), QAbstractItemView::ScrollHint::PositionAtTop); } else { ui.twCategories->scrollToItem(ui.twCategories->topLevelItem(topItemIdx), QAbstractItemView::ScrollHint::PositionAtTop); } } } } /** * @brief Scrolls the lwDatasets to the given dataset name. * @param rootName the name of the dataset */ void ImportDatasetWidget::scrollToDatasetListItem(const QString& rootName) { int itemIdx = -1; for (int i = 0; i < ui.lwDatasets->count(); ++i) if (ui.lwDatasets->item(i)->text() == rootName) { itemIdx = i; break; } if (itemIdx >= 0) ui.lwDatasets->scrollToItem(ui.lwDatasets->item(itemIdx), QAbstractItemView::ScrollHint::PositionAtTop); } /** * @brief Returns the name of the selected dataset */ QString ImportDatasetWidget::getSelectedDataset() const { if (ui.lwDatasets->selectedItems().count() > 0) return ui.lwDatasets->selectedItems().at(0)->text(); else return QString(); } /** * @brief Initiates the processing of the dataset's metadata file and of the dataset itself. * @param datasetHandler the DatasetHanlder that downloads processes the dataset */ void ImportDatasetWidget::loadDatasetToProcess(DatasetHandler* datasetHandler) { const QString fileName = getSelectedDataset() + QLatin1String(".json"); QString filePath = m_jsonDir; QJsonObject datasetObject = loadDatasetObject(); if(!datasetObject.isEmpty()) { datasetHandler->processMetadata(datasetObject, filePath); } else { QMessageBox::critical(this, i18n("Invalid metadata file"), i18n("The metadata file for the choosen dataset isn't valid")); } } /** * @brief Returns the QJsonObject associated with the currently selected dataset. */ QJsonObject ImportDatasetWidget::loadDatasetObject() { QString filePath = m_jsonDir + "DatasetCollections.json"; QFile file(filePath); bool allCollections = (m_selectedCollection.compare("All") == 0); if (file.open(QIODevice::ReadOnly)) { QJsonDocument document = QJsonDocument::fromJson(file.readAll()); QJsonArray collections; if(document.isArray()) collections = document.array(); else { qDebug()<< "The DatasetCollections.json file is invalid"; return QJsonObject(); } for (int collectionIndex = 0; collectionIndex < collections.size(); collectionIndex++) { const QString currentCollection = collections[collectionIndex].toString(); //we have to find the selected collection in the metadata file. if(currentCollection.compare(m_selectedCollection) == 0 || allCollections) { QFile collectionFile(m_jsonDir + currentCollection + ".json"); //open the metadata file of the current collection if (collectionFile.open(QIODevice::ReadOnly)) { QJsonDocument collectionDocument = QJsonDocument::fromJson(collectionFile.readAll()); QJsonObject collectionObject; if(collectionDocument.isObject()) { collectionObject = collectionDocument.object(); } else { qDebug()<< "The " + currentCollection + ".json file is invalid"; return QJsonObject(); } if(collectionObject.value("collection_name").toString().compare(currentCollection) != 0) { qDebug()<< "The " + currentCollection + ".json file's name is invalid"; return QJsonObject(); } QJsonArray categoryArray = collectionObject.value("categories").toArray(); //processing categories for(int i = 0 ; i < categoryArray.size(); ++i) { const QJsonObject currentCategory = categoryArray[i].toObject(); const QString categoryName = currentCategory.value("category_name").toString(); if(categoryName.compare(m_selectedCategory) == 0) { const QJsonArray subcategories = currentCategory.value("subcategories").toArray(); //processing subcategories for(int j = 0; j < subcategories.size(); ++j) { QJsonObject currentSubCategory = subcategories[j].toObject(); QString subcategoryName = currentSubCategory.value("subcategory_name").toString(); if(subcategoryName.compare(m_selectedSubcategory) == 0) { const QJsonArray datasetArray = currentSubCategory.value("datasets").toArray(); //processing the datasets o the actual subcategory for (const auto& dataset : datasetArray) { if(getSelectedDataset().compare(dataset.toObject().value("filename").toString()) == 0) return dataset.toObject(); } } } } } } } } } return QJsonObject(); } /** * @brief Opens the DatasetMetadataManagerDialog when the user wants to add a new dataset. */ void ImportDatasetWidget::showDatasetMetadataManager() { DatasetMetadataManagerDialog* dlg = new DatasetMetadataManagerDialog(this, m_datasetsMap); if (dlg->exec() == QDialog::Accepted) { const QString pathToJson = m_jsonDir + QLatin1String("DatasetCategories.json"); const QString dirPath = QFileInfo(pathToJson).dir().absolutePath(); //update the metadata document dlg->updateDocument(m_jsonDir); //Not working due to problems with KNS3 library /*uploadCategoryFile(); uploadDatasetFile(dlg->getMetadataFilePath());*/ //process the changes made in the metadata files loadDatasetCategoriesFromJson(); } delete dlg; } /** - * @brief Places the metadata file containing the categories and subcategories into a specific directory. + * @brief Places the metadata file containing the list of collections into a specific directory. */ void ImportDatasetWidget::downloadCollectionsFile() { const QString fileNameOld = QStandardPaths::locate(QStandardPaths::AppDataLocation, "datasets/DatasetCollections.json"); const QString fileNameNew =m_jsonDir + QLatin1String("DatasetCollections.json"); const QString parentDir = m_jsonDir.left(m_jsonDir.left(m_jsonDir.length() - 1).lastIndexOf(QDir::separator())); if(!QDir(m_jsonDir).exists()) { qDebug() << parentDir; QDir(parentDir).mkdir(QLatin1String("labplot_data")); } QFile::copy(fileNameOld, fileNameNew); } /** * @brief Places the metadata file of the given dataset into a specific directory. * @param datasetName the name of the dataset */ void ImportDatasetWidget::downloadCollectionFile(const QString& collectionName) { const QString fileNameOld = QStandardPaths::locate(QStandardPaths::AppDataLocation, QLatin1String("datasets") + QDir::separator() + collectionName); const QString fileNameNew =m_jsonDir + collectionName; QFile::copy(fileNameOld, fileNameNew); } /** * @brief Refreshes the categories, subcategories and datasets. */ void ImportDatasetWidget::refreshCategories() { - QString fileNameNew = m_jsonDir + QLatin1String("DatasetCollections.json"); + QMessageBox::StandardButton reply; + reply = QMessageBox::question(this, "Refresh metadata files", + "Are you sure to refresh all of the metadata files? (every change will be removed, but a backup will be created)", QMessageBox::Yes|QMessageBox::No); + + if(reply == QMessageBox::Yes) { + QString fileNameNew = m_jsonDir + QLatin1String("DatasetCollections.json"); - QFile existingCategoriesFile(fileNameNew); - if(existingCategoriesFile.exists()) { + QFile existingCategoriesFile(fileNameNew); + if(existingCategoriesFile.exists()) { + + //Delete old backup + QFile oldBackup(m_jsonDir + QLatin1String("DatasetCollections_backup.json")); + if(oldBackup.exists()) { + oldBackup.remove(); + } + oldBackup.close(); - //Delete old backup - QFile oldBackup(m_jsonDir + QLatin1String("DatasetCollections_backup.json")); - if(oldBackup.exists()) { - oldBackup.remove(); + //Create new backup + if(!existingCategoriesFile.rename(m_jsonDir + QLatin1String("DatasetCollections_backup.json"))) + qDebug() << " Couldn't create backup because " << existingCategoriesFile.errorString(); } - oldBackup.close(); - //Create new backup - if(!existingCategoriesFile.rename(m_jsonDir + QLatin1String("DatasetCollections_backup.json"))) - qDebug() << " Couldn't create backup because " << existingCategoriesFile.errorString(); - } + //Obtain the new file + downloadCollectionsFile(); - //Obtain the new file - downloadCollectionsFile(); + QString filePath = m_jsonDir + "DatasetCollections.json"; + QFile file(filePath); - QString filePath = m_jsonDir + "DatasetCollections.json"; - QFile file(filePath); + if (file.open(QIODevice::ReadOnly)) { + m_datasetsMap.clear(); - if (file.open(QIODevice::ReadOnly)) { - m_datasetsMap.clear(); + QJsonDocument document = QJsonDocument::fromJson(file.readAll()); + QJsonArray collections; + if(document.isArray()) + collections = document.array(); + else { + qDebug()<< "The DatasetCollections.json file is invalid"; + return; + } - QJsonDocument document = QJsonDocument::fromJson(file.readAll()); - QJsonArray collections; - if(document.isArray()) - collections = document.array(); - else { - qDebug()<< "The DatasetCollections.json file is invalid"; - return; + //Go trough every collection's metadata file + for (int collectionIndex = 0; collectionIndex < collections.size(); collectionIndex++) { + const QString currentCollection = collections[collectionIndex].toString(); + + QFile existingCollectionFile(m_jsonDir + currentCollection + ".json"); + //we copy the file to the data location if it doesn't exist + if(!existingCollectionFile.exists()) { + downloadCollectionFile(currentCollection + ".json"); + } + //otherwise we have to create a backup first + else { + QFile oldBackupCollection(m_jsonDir + currentCollection + "_backup.json"); + if(oldBackupCollection.exists()) { + oldBackupCollection.remove(); + } + oldBackupCollection.close(); + + if(!existingCollectionFile.rename(m_jsonDir + currentCollection + "_backup.json")) + qDebug() << " Couldn't create backup because " << existingCollectionFile.errorString(); + + downloadCollectionFile(currentCollection + ".json"); + } + } } - //Go trough every collection's metadata file - for (int collectionIndex = 0; collectionIndex < collections.size(); collectionIndex++) { - const QString currentCollection = collections[collectionIndex].toString(); + //process the "refreshed" files and update the widget accordingly + loadDatasetCategoriesFromJson(); + } +} - QFile existingCollectionFile(m_jsonDir + currentCollection + ".json"); - //we copy the file to the data location if it doesn't exist - if(!existingCollectionFile.exists()) { - downloadCollectionFile(currentCollection + ".json"); +void ImportDatasetWidget::restoreBackup() { + QMessageBox::StandardButton reply; + reply = QMessageBox::question(this, "Restore backup", "Are you sure to restore the backup metadata files?", QMessageBox::Yes|QMessageBox::No); + + if(reply == QMessageBox::Yes) { + //Restore the collection list first + QFile backup(m_jsonDir + QLatin1String("DatasetCollections_backup.json")); + if(backup.exists()) { + QFile deleteFile(m_jsonDir + QLatin1String("DatasetCollections.json")); + deleteFile.remove(); + + if(!backup.rename(m_jsonDir + QLatin1String("DatasetCollections.json"))) { + qDebug() << " Couldn't create backup because " << backup.errorString(); + downloadCollectionsFile(); } - //otherwise we have to create a backup first + } + + QString filePath = m_jsonDir + "DatasetCollections.json"; + QFile file(filePath); + if (file.open(QIODevice::ReadOnly)) { + m_datasetsMap.clear(); + QJsonDocument document = QJsonDocument::fromJson(file.readAll()); + QJsonArray collections; + if(document.isArray()) + collections = document.array(); else { - QFile oldBackupCollection(m_jsonDir + currentCollection + "_backup.json"); - if(oldBackupCollection.exists()) { - oldBackupCollection.remove(); - } - oldBackupCollection.close(); + qDebug()<< "The DatasetCollections.json file is invalid"; + return; + } - if(!existingCollectionFile.rename(m_jsonDir + currentCollection + "_backup.json")) - qDebug() << " Couldn't create backup because " << existingCollectionFile.errorString(); + //Restore every collection's metadata file + for (int collectionIndex = 0; collectionIndex < collections.size(); collectionIndex++) { + const QString currentCollection = collections[collectionIndex].toString(); - downloadCollectionFile(currentCollection + ".json"); + QFile backupCollection(m_jsonDir + currentCollection + "_backup.json"); + if(backupCollection.exists()) { + QFile collectionFile(m_jsonDir + currentCollection + ".json"); + collectionFile.remove(); + + if(!backupCollection.rename(m_jsonDir + currentCollection + ".json")) { + qDebug() << " Couldn't create backup because " << backupCollection.errorString(); + downloadCollectionFile(currentCollection + ".json"); + } + } } } - } - //process the "refreshed" files and update the widget accordingly - loadDatasetCategoriesFromJson(); + //process the restored files and update the widget accordingly + loadDatasetCategoriesFromJson(); + } } /** * @brief Clears the content of the directory in which the download of metadata files was done. */ void ImportDatasetWidget::clearCache() { - QDir dir(m_jsonDir); - - if(dir.exists()) { - for(const auto& entry : dir.entryList()) { - //delete every file that isn't potentially a metadata file - if(!(entry.endsWith(QLatin1String(".json")) || entry.startsWith(QLatin1Char('.')))) { - QFile deleteFile (m_jsonDir + entry); - if(deleteFile.exists()) { - deleteFile.remove(); + QMessageBox::StandardButton reply; + reply = QMessageBox::question(this, "Clear cache", "Are you sure to remove every downloaded dataset?", QMessageBox::Yes|QMessageBox::No); + + if(reply == QMessageBox::Yes) { + QDir dir(m_jsonDir); + + if(dir.exists()) { + for(const auto& entry : dir.entryList()) { + //delete every file that isn't potentially a metadata file + if(!(entry.endsWith(QLatin1String(".json")) || entry.startsWith(QLatin1Char('.')))) { + QFile deleteFile (m_jsonDir + entry); + if(deleteFile.exists()) { + deleteFile.remove(); + } } } + } else { + qDebug("Couldn't clear cache, containing folder doesn't exist!"); } - } else { - qDebug("Couldn't clear cache, containing folder doesn't exist!"); - } - highlightLocalMetadataFiles(); + highlightLocalMetadataFiles(); + } } /** * @brief Highlights the name of the locally available metadata files in lwDatasets. */ void ImportDatasetWidget::highlightLocalMetadataFiles() { QDir dir(m_jsonDir); for(int i = 0 ; i < ui.lwDatasets->count(); ++i) { QListWidgetItem* const currentItem = ui.lwDatasets->item(i); bool found = false; for(QString entry : dir.entryList()) { if(entry.startsWith(currentItem->text()) && !entry.endsWith(".json")) { found = true; break; } } if(found) currentItem->setBackgroundColor(Qt::yellow); else currentItem->setBackgroundColor(Qt::white); } } /** * @brief TODO: uploads the metadata file that contains the categories to store.kde.org -- Library doesn't work for indefinite time. */ void ImportDatasetWidget::uploadCategoryFile() { /*KNS3::UploadDialog dialog("labplot2_datasets.knsrc", this); QFile file(m_jsonDir + "DatasetCategories.json"); qDebug() << "file " << m_jsonDir + "DatasetCategories.json "<< file.exists(); qDebug() << "file can be opened: " << file.open(QIODevice::ReadOnly) << " " << file.errorString(); file.close(); QUrl payloadFile ="file:" + m_jsonDir + "DatasetCategories.json"; QFile file2(payloadFile.toLocalFile()); qDebug() << "Local file: " << payloadFile.toLocalFile(); if (!file2.open(QIODevice::ReadOnly)) { qDebug() << i18n("File not found: %1 ", payloadFile.url()); } else { qDebug() << i18n("File found: %1 ", payloadFile.url()); } file2.close(); dialog.setUploadFile("file:" + m_jsonDir + "DatasetCategories.json"); qDebug("Upload file set!"); dialog.setUploadName("Dataset Categories"); qDebug() << "Upload name set: "; dialog.exec();*/ } /** * @brief TODO: uploads the metadata file of a dataset to store.kde.org -- Library doesn't work for indefinite time. */ void ImportDatasetWidget::uploadDatasetFile(const QString& filePath) { /*KNS3::UploadDialog dialog("labplot2_datasets.knsrc", this); QFile file(filePath); qDebug() << filePath + " " << file.exists(); qDebug() << "file can be opened: " << file.open(QIODevice::ReadOnly) << " " << file.errorString(); file.close(); QUrl payloadFile ="file:" + filePath; QFile file2(payloadFile.toLocalFile()); qDebug() << "Local file: " << payloadFile.toLocalFile(); if (!file2.open(QIODevice::ReadOnly)) { qDebug() << i18n("File not found: %1 ", payloadFile.url()); } else { qDebug() << i18n("File found: %1 ", payloadFile.url()); } file2.close(); dialog.setUploadFile("file:" + filePath); qDebug("Upload file set!"); dialog.setUploadName("Dataset Categories"); qDebug() << "Upload name set: "; dialog.exec();*/ } /** * @brief Returns the structure containing the categories, subcategories and datasets. * @return the structure containing the categories, subcategories and datasets */ const QMap< QString, QMap>>>& ImportDatasetWidget::getDatasetsMap() { return m_datasetsMap; } /** * @brief Sets the currently selected collection * @param category the name of the collection */ void ImportDatasetWidget::setCollection(const QString& collection) { ui.cbCollections->setCurrentText(collection + " (" + QString(m_datasetModel->datasetCount(collection)) + ")"); } /** * @brief Sets the currently selected category * @param category the name of the category */ void ImportDatasetWidget::setCategory(const QString &category) { for(int i = 0; i < ui.twCategories->topLevelItemCount(); i++) { if (ui.twCategories->topLevelItem(i)->text(0).compare(category) == 0) { listDatasetsForSubcategory(ui.twCategories->topLevelItem(i)); break; } } } /** * @brief Sets the currently selected subcategory * @param subcategory the name of the subcategory */ void ImportDatasetWidget::setSubcategory(const QString &subcategory) { for(int i = 0; i < ui.twCategories->topLevelItemCount(); i++) { if (ui.twCategories->topLevelItem(i)->text(0).compare(m_selectedCategory) == 0) { QTreeWidgetItem* categoryItem = ui.twCategories->topLevelItem(i); for(int j = 0; j childCount(); j++) { if(categoryItem->child(j)->text(0).compare(subcategory) == 0) { listDatasetsForSubcategory(categoryItem->child(j)); break; } } break; } } } /** * @brief Sets the currently selected dataset * @param the currently selected dataset */ void ImportDatasetWidget::setDataset(const QString &datasetName) { for(int i = 0; i < ui.lwDatasets->count() ; i++) { if(ui.lwDatasets->item(i)->text().compare(datasetName) == 0) { ui.lwDatasets->item(i)->setSelected(true); break; } } } /** * @brief Updates the details of the currently selected dataset */ void ImportDatasetWidget::updateDetails() { if(!getSelectedDataset().isEmpty()) { QJsonObject datasetObject = loadDatasetObject(); ui.lFullName->setText(datasetObject.value("name").toString()); ui.lDescription->setText(datasetObject.value("description").toString()); } else { ui.lFullName->setText("-"); ui.lDescription->setText("-"); } } /** * @brief Hides or displays the details of the currently selected dataset * @param show boolean value determining to show or not the details. */ void ImportDatasetWidget::showDetails(bool show) { ui.saDetails->setEnabled(show); ui.saDetails->setVisible(show); if(show) updateDetails(); } /** * @brief Returns the directory in which the metadata json files are locating. */ const QString ImportDatasetWidget::getJsonDir() { return m_jsonDir; } diff --git a/src/kdefrontend/datasources/ImportDatasetWidget.h b/src/kdefrontend/datasources/ImportDatasetWidget.h index 91c1d25a0..18045a5a9 100644 --- a/src/kdefrontend/datasources/ImportDatasetWidget.h +++ b/src/kdefrontend/datasources/ImportDatasetWidget.h @@ -1,102 +1,103 @@ /*************************************************************************** File : ImportDatasetWidget.h Project : LabPlot Description : import online dataset widget -------------------------------------------------------------------- Copyright : (C) 2019 Kovacs Ferencz (kferike98@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #ifndef IMPORTDATASETWIDGET_H #define IMPORTDATASETWIDGET_H #include "ui_importdatasetwidget.h" #include "QWidget" #include "QMap" class QCompleter; class DatasetHandler; class DatasetModel; class ImportDatasetWidget : public QWidget { Q_OBJECT public: explicit ImportDatasetWidget(QWidget* parent); ~ImportDatasetWidget() override; QString getSelectedDataset() const; void loadDatasetToProcess(DatasetHandler* datasetHandler); QString locateCategoryJsonFile() const; const QMap< QString, QMap>>>& getDatasetsMap(); void setCollection(const QString&); void setCategory(const QString&); void setSubcategory(const QString&); void setDataset(const QString&); void processTest(const QString& category, const QString& subcategory, const QString& dataset, DatasetHandler* datasetHandler); const QString getJsonDir(); private: Ui::ImportDatasetWidget ui; QMap< QString, QMap>>> m_datasetsMap; QString m_selectedSubcategory; QCompleter* m_categoryCompleter; QCompleter* m_datasetCompleter; QString m_jsonDir; bool m_loadingCategories; QString m_selectedCategory; QString m_selectedCollection; DatasetModel* m_datasetModel{nullptr}; bool m_showDetails{false}; QString validCollectionName(const QString& collection); void downloadCollectionsFile(); void downloadCollectionFile(const QString&); void uploadCategoryFile(); void uploadDatasetFile(const QString&); void updateDatasetCompleter(); void updateCategoryCompleter(); void loadDatasetCategoriesFromJson(); void listDatasetsForSubcategory(QTreeWidgetItem* item); void restoreSelectedSubcategory(const QString& collection); void highlightLocalMetadataFiles(); QJsonObject loadDatasetObject(); void updateDetails(); void showDetails(bool show); private slots: void updateCategoryTree(const QString&); void scrollToCategoryTreeItem(const QString& rootName); void scrollToDatasetListItem(const QString& rootName); void showDatasetMetadataManager(); void refreshCategories(); void clearCache(); + void restoreBackup(); signals: void datasetSelected(); void datasetDoubleClicked(); }; #endif // IMPORTDATASETWIDGET_H diff --git a/src/kdefrontend/ui/datasources/datasetmetadatamanagerwidget.ui b/src/kdefrontend/ui/datasources/datasetmetadatamanagerwidget.ui index 7de61987b..5dec49d63 100644 --- a/src/kdefrontend/ui/datasources/datasetmetadatamanagerwidget.ui +++ b/src/kdefrontend/ui/datasources/datasetmetadatamanagerwidget.ui @@ -1,263 +1,283 @@ DatasetMetadataManagerWidget 0 0 457 - 567 + 575 Form true Category Column Descriptions Delete Description Add new description Qt::Horizontal 40 20 Filter Options Comment character: Separating string: Number format: DateTime format: Use the first row to name the vectors true Create index column Removes the whitespaces from the start and the end, and replaces each sequence of internal whitespaces with a single space. Simplify whitespaces true Skip empty parts Remove quotes 0 0 true true 0 0 true Short name Download URL Description Subcategory true Full name Collection true KComboBox QComboBox
kcombobox.h
+ + cbCollection + cbCategory + cbSubcategory + leFileName + leDatasetName + leDownloadURL + teDescription + bNewColumn + bDelete + cbCommentCharacter + cbSeparatingCharacter + cbNumberFormat + cbDateTimeFormat + chbCreateIndex + chbSimplifyWhitespaces + chbSkipEmptyParts + chbRemoveQuotes + chbHeader +
diff --git a/src/kdefrontend/ui/datasources/importdatasetwidget.ui b/src/kdefrontend/ui/datasources/importdatasetwidget.ui index eaa89d918..c225e5c9c 100644 --- a/src/kdefrontend/ui/datasources/importdatasetwidget.ui +++ b/src/kdefrontend/ui/datasources/importdatasetwidget.ui @@ -1,241 +1,266 @@ ImportDatasetWidget 0 0 - 380 + 387 641 0 0 Form + + + + + 0 + 0 + + + + + + + + Categories + + + Refresh + + + + Restore + + + Add new dataset Clear cache - - - - Search datasets - - - - - - - Collections - - - - - - - - - - - - - - 0 - 0 - - - - - Category Name - - - - - - - - - 0 - 0 - - - - - - - - Search categories - - - - - - - Categories - - - - - - - - 0 - 0 - - - - Datasets - - - Show details 0 0 Qt::ScrollBarAlwaysOff true 0 0 - 360 + 367 68 0 0 - Qt::RichText Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true 0 0 - Qt::RichText Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true 0 0 Description: Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop false 0 0 Full name: Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + 0 + 0 + + + + Datasets + + + + + + + + + + Search categories + + + + + + + Search datasets + + + + + + + Collections + + + + + + + + + + + 0 + 0 + + + + + Category Name + + + + + Dataset Count + + + + + + cbCollections + twCategories + leSearchCategories + lwDatasets + leSearchDatasets + bShowDetails + bRefresh + bRestore + bNewDataset + bClearCache + saDetails +