From 8e86655beb09ab0af93c6df0415bb9118a27fecc Mon Sep 17 00:00:00 2001 From: Martin Rotter Date: Tue, 5 Jan 2016 08:12:15 +0100 Subject: [PATCH] Work on better parsing of input import data. --- src/gui/widgetwithstatus.cpp | 5 ++ src/gui/widgetwithstatus.h | 4 +- .../standard/gui/formstandardimportexport.cpp | 59 ++++++++++++------ .../standard/gui/formstandardimportexport.h | 4 ++ .../standard/gui/formstandardimportexport.ui | 10 +++ src/services/standard/standardfeed.cpp | 4 +- src/services/standard/standardfeed.h | 4 +- .../standardfeedsimportexportmodel.cpp | 62 +++++++++++++------ .../standard/standardfeedsimportexportmodel.h | 13 +++- 9 files changed, 122 insertions(+), 43 deletions(-) mode change 100644 => 100755 src/gui/widgetwithstatus.h diff --git a/src/gui/widgetwithstatus.cpp b/src/gui/widgetwithstatus.cpp index 4b71fdf42..257f2c9a3 100755 --- a/src/gui/widgetwithstatus.cpp +++ b/src/gui/widgetwithstatus.cpp @@ -29,6 +29,7 @@ WidgetWithStatus::WidgetWithStatus(QWidget *parent) m_btnStatus = new PlainToolButton(this); m_btnStatus->setFocusPolicy(Qt::NoFocus); + m_iconProgress = qApp->icons()->fromTheme(QSL("item-sync")); m_iconInformation = qApp->icons()->fromTheme(QSL("dialog-information")); m_iconWarning = qApp->icons()->fromTheme(QSL("dialog-warning")); m_iconError = qApp->icons()->fromTheme(QSL("dialog-error")); @@ -52,6 +53,10 @@ void WidgetWithStatus::setStatus(WidgetWithStatus::StatusType status, const QStr m_btnStatus->setIcon(m_iconInformation); break; + case Progress: + m_btnStatus->setIcon(m_iconProgress); + break; + case Warning: m_btnStatus->setIcon(m_iconWarning); break; diff --git a/src/gui/widgetwithstatus.h b/src/gui/widgetwithstatus.h old mode 100644 new mode 100755 index 5bf6b0e66..4afd3b487 --- a/src/gui/widgetwithstatus.h +++ b/src/gui/widgetwithstatus.h @@ -33,7 +33,8 @@ class WidgetWithStatus : public QWidget { Information, Warning, Error, - Ok + Ok, + Progress }; // Constructors and destructors. @@ -53,6 +54,7 @@ class WidgetWithStatus : public QWidget { PlainToolButton *m_btnStatus; QHBoxLayout *m_layout; + QIcon m_iconProgress; QIcon m_iconInformation; QIcon m_iconWarning; QIcon m_iconError; diff --git a/src/services/standard/gui/formstandardimportexport.cpp b/src/services/standard/gui/formstandardimportexport.cpp index 0caa17475..9b508303f 100755 --- a/src/services/standard/gui/formstandardimportexport.cpp +++ b/src/services/standard/gui/formstandardimportexport.cpp @@ -36,6 +36,10 @@ FormStandardImportExport::FormStandardImportExport(StandardServiceRoot *service_ m_ui->setupUi(this); m_model = new FeedsImportExportModel(m_ui->m_treeFeeds); + connect(m_model, SIGNAL(parsingStarted()), this, SLOT(onParsingStarted())); + connect(m_model, SIGNAL(parsingFinished(int,int,bool)), this, SLOT(onParsingFinished(int,int,bool))); + connect(m_model, SIGNAL(parsingProgress(int,int)), this, SLOT(onParsingProgress(int,int))); + setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint); m_ui->m_lblSelectFile->setStatus(WidgetWithStatus::Error, tr("No file is selected."), tr("No file is selected.")); @@ -100,6 +104,41 @@ void FormStandardImportExport::selectFile() { } } +void FormStandardImportExport::onParsingStarted() { + m_ui->m_lblResult->setStatus(WidgetWithStatus::Progress, tr("Parsing data..."), tr("Parsing data...")); + m_ui->m_btnSelectFile->setEnabled(false); + m_ui->m_progressBar->setEnabled(true); + m_ui->m_progressBar->setValue(0); +} + +void FormStandardImportExport::onParsingFinished(int count_failed, int count_succeeded, bool parsing_error) { + Q_UNUSED(count_failed) + Q_UNUSED(count_succeeded) + + m_ui->m_progressBar->setValue(0); + m_ui->m_progressBar->setEnabled(false); + m_model->checkAllItems(); + + if (!parsing_error) { + m_ui->m_lblResult->setStatus(WidgetWithStatus::Ok, tr("Feeds were loaded."), tr("Feeds were loaded.")); + m_ui->m_groupFeeds->setEnabled(true); + m_ui->m_btnSelectFile->setEnabled(true); + m_ui->m_treeFeeds->setModel(m_model); + m_ui->m_treeFeeds->expandAll(); + } + else { + m_ui->m_lblResult->setStatus(WidgetWithStatus::Error, tr("Error, file is not well-formed. Select another file."), + tr("Error occurred. File is not well-formed. Select another file.")); + } + + m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!parsing_error); +} + +void FormStandardImportExport::onParsingProgress(int completed, int total) { + m_ui->m_progressBar->setMaximum(total); + m_ui->m_progressBar->setValue(completed); +} + void FormStandardImportExport::selectExportFile() { QString filter_opml20 = tr("OPML 2.0 files (*.opml)"); QString filter_txt_url_per_line = tr("TXT files (one URL per line) (*.txt)"); @@ -162,7 +201,6 @@ void FormStandardImportExport::selectImportFile() { m_ui->m_lblSelectFile->setStatus(WidgetWithStatus::Ok, QDir::toNativeSeparators(selected_file), tr("File is selected.")); parseImportFile(selected_file); - m_model->checkAllItems(); } } @@ -179,15 +217,13 @@ void FormStandardImportExport::parseImportFile(const QString &file_name) { return; } - bool parsing_result; - switch (m_conversionType) { case OPML20: - parsing_result = m_model->importAsOPML20(input_data); + m_model->importAsOPML20(input_data); break; case TXTUrlPerLine: - parsing_result = m_model->importAsTxtURLPerLine(input_data); + m_model->importAsTxtURLPerLine(input_data); break; // TODO: V celém kódu nově zavést pořádně všude const, i v lokálních metodových proměnných @@ -198,19 +234,6 @@ void FormStandardImportExport::parseImportFile(const QString &file_name) { default: return; } - - if (parsing_result) { - m_ui->m_lblResult->setStatus(WidgetWithStatus::Ok, tr("Feeds were loaded."), tr("Feeds were loaded.")); - m_ui->m_groupFeeds->setEnabled(true); - m_ui->m_treeFeeds->setModel(m_model); - m_ui->m_treeFeeds->expandAll(); - } - else { - m_ui->m_lblResult->setStatus(WidgetWithStatus::Error, tr("Error, file is not well-formed. Select another file."), - tr("Error occurred. File is not well-formed. Select another file.")); - } - - m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(parsing_result); } void FormStandardImportExport::performAction() { diff --git a/src/services/standard/gui/formstandardimportexport.h b/src/services/standard/gui/formstandardimportexport.h index 237ff08e1..209c20800 100755 --- a/src/services/standard/gui/formstandardimportexport.h +++ b/src/services/standard/gui/formstandardimportexport.h @@ -48,6 +48,10 @@ class FormStandardImportExport : public QDialog { void performAction(); void selectFile(); + void onParsingStarted(); + void onParsingFinished(int count_failed, int count_succeeded, bool parsing_error); + void onParsingProgress(int completed, int total); + private: void selectExportFile(); void selectImportFile(); diff --git a/src/services/standard/gui/formstandardimportexport.ui b/src/services/standard/gui/formstandardimportexport.ui index 53b635d74..671a9dd2d 100755 --- a/src/services/standard/gui/formstandardimportexport.ui +++ b/src/services/standard/gui/formstandardimportexport.ui @@ -114,6 +114,16 @@ + + + + false + + + 0 + + + diff --git a/src/services/standard/standardfeed.cpp b/src/services/standard/standardfeed.cpp index f7a200997..297ce3480 100755 --- a/src/services/standard/standardfeed.cpp +++ b/src/services/standard/standardfeed.cpp @@ -282,7 +282,9 @@ void StandardFeed::fetchMetadataForItself() { } } -QPair StandardFeed::guessFeed(const QString &url, const QString &username, const QString &password) { +QPair StandardFeed::guessFeed(const QString &url, + const QString &username, + const QString &password) { QPair result; result.first = NULL; QByteArray feed_contents; diff --git a/src/services/standard/standardfeed.h b/src/services/standard/standardfeed.h index f11b6afd6..303c66916 100755 --- a/src/services/standard/standardfeed.h +++ b/src/services/standard/standardfeed.h @@ -145,7 +145,9 @@ class StandardFeed : public Feed { // Returns pointer to guessed feed (if at least partially // guessed) and retrieved error/status code from network layer // or NULL feed. - static QPair guessFeed(const QString &url, const QString &username, const QString &password); + static QPair guessFeed(const QString &url, + const QString &username = QString(), + const QString &password = QString()); // Converts particular feed type to string. static QString typeToString(Type type); diff --git a/src/services/standard/standardfeedsimportexportmodel.cpp b/src/services/standard/standardfeedsimportexportmodel.cpp index 5702c2616..fb57a9d90 100755 --- a/src/services/standard/standardfeedsimportexportmodel.cpp +++ b/src/services/standard/standardfeedsimportexportmodel.cpp @@ -154,19 +154,22 @@ bool FeedsImportExportModel::exportToOMPL20(QByteArray &result) { return true; } -bool FeedsImportExportModel::importAsOPML20(const QByteArray &data) { +void FeedsImportExportModel::importAsOPML20(const QByteArray &data) { + emit parsingStarted(); + QDomDocument opml_document; if (!opml_document.setContent(data)) { - return false; + emit parsingFinished(0, 0, true); } if (opml_document.documentElement().isNull() || opml_document.documentElement().tagName() != QSL("opml") || opml_document.documentElement().elementsByTagName(QSL("body")).size() != 1) { // This really is not an OPML file. - return false; + emit parsingFinished(0, 0, true); } + int completed = 0, total = 0; StandardServiceRoot *root_item = new StandardServiceRoot(); QStack model_items; model_items.push(root_item); QStack elements_to_process; elements_to_process.push(opml_document.documentElement().elementsByTagName(QSL("body")).at(0).toElement()); @@ -175,7 +178,10 @@ bool FeedsImportExportModel::importAsOPML20(const QByteArray &data) { RootItem *active_model_item = model_items.pop(); QDomElement active_element = elements_to_process.pop(); - for (int i = 0; i < active_element.childNodes().size(); i++) { + int current_count = active_element.childNodes().size(); + total += current_count; + + for (int i = 0; i < current_count; i++) { QDomNode child = active_element.childNodes().at(i); if (child.isElement()) { @@ -242,6 +248,8 @@ bool FeedsImportExportModel::importAsOPML20(const QByteArray &data) { elements_to_process.push(child_element); model_items.push(new_category); } + + emit parsingProgress(++completed, total); } } } @@ -250,8 +258,7 @@ bool FeedsImportExportModel::importAsOPML20(const QByteArray &data) { emit layoutAboutToBeChanged(); setRootItem(root_item); emit layoutChanged(); - - return true; + emit parsingFinished(0, completed, false); } bool FeedsImportExportModel::exportToTxtURLPerLine(QByteArray &result) { @@ -262,32 +269,49 @@ bool FeedsImportExportModel::exportToTxtURLPerLine(QByteArray &result) { return true; } -bool FeedsImportExportModel::importAsTxtURLPerLine(const QByteArray &data) { +void FeedsImportExportModel::importAsTxtURLPerLine(const QByteArray &data) { + emit parsingStarted(); + + int completed = 0, succeded = 0, failed = 0; StandardServiceRoot *root_item = new StandardServiceRoot(); + QList urls = data.split('\n'); - foreach (const QByteArray &url, data.split('\n')) { + foreach (const QByteArray &url, urls) { if (!url.isEmpty()) { + QPair guessed = StandardFeed::guessFeed(url); - StandardFeed *feed = new StandardFeed(); + if (guessed.second == QNetworkReply::NoError) { + guessed.first->setUrl(url); + root_item->appendChild(guessed.first); + succeded++; + } + else { + StandardFeed *feed = new StandardFeed(); - // TODO: co guessovat ten feed? + feed->setUrl(url); + feed->setTitle(url); + feed->setCreationDate(QDateTime::currentDateTime()); + feed->setIcon(qApp->icons()->fromTheme(QSL("folder-feed"))); + feed->setEncoding(DEFAULT_FEED_ENCODING); + root_item->appendChild(feed); + failed++; + } - feed->setUrl(url); - feed->setTitle(url); - feed->setCreationDate(QDateTime::currentDateTime()); - feed->setIcon(qApp->icons()->fromTheme(QSL("folder-feed"))); - feed->setEncoding(DEFAULT_FEED_ENCODING); - - root_item->appendChild(feed); + qApp->processEvents(); } + else { + qWarning("Detected empty URL when parsing input TXT (one URL per line) data."); + failed++; + } + + emit parsingProgress(++completed, urls.size()); } // Now, XML is processed and we have result in form of pointer item structure. emit layoutAboutToBeChanged(); setRootItem(root_item); emit layoutChanged(); - - return true; + emit parsingFinished(failed, succeded, false); } FeedsImportExportModel::Mode FeedsImportExportModel::mode() const { diff --git a/src/services/standard/standardfeedsimportexportmodel.h b/src/services/standard/standardfeedsimportexportmodel.h index 18b1ed01f..5341c8aa2 100755 --- a/src/services/standard/standardfeedsimportexportmodel.h +++ b/src/services/standard/standardfeedsimportexportmodel.h @@ -60,12 +60,12 @@ class FeedsImportExportModel : public QAbstractItemModel { // Exports to OPML 2.0 // NOTE: http://dev.opml.org/spec2.html bool exportToOMPL20(QByteArray &result); - bool importAsOPML20(const QByteArray &data); + void importAsOPML20(const QByteArray &data); // Exports to plain text format // where there is one feed URL per line. bool exportToTxtURLPerLine(QByteArray &result); - bool importAsTxtURLPerLine(const QByteArray &data); + void importAsTxtURLPerLine(const QByteArray &data); Mode mode() const; void setMode(const Mode &mode); @@ -74,8 +74,15 @@ class FeedsImportExportModel : public QAbstractItemModel { void checkAllItems(); void uncheckAllItems(); + signals: + // These signals are emitted when user selects some data + // to be imported/parsed into the model. + void parsingStarted(); + void parsingProgress(int completed, int total); + void parsingFinished(int count_failed, int count_succeeded, bool parsing_error); + private: - QHash m_checkStates; + QHash m_checkStates; RootItem *m_rootItem; bool m_recursiveChange;