Work on better parsing of input import data.

This commit is contained in:
Martin Rotter 2016-01-05 08:12:15 +01:00
parent 22a6da1604
commit 8e86655beb
9 changed files with 122 additions and 43 deletions

View File

@ -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;

4
src/gui/widgetwithstatus.h Normal file → Executable file
View File

@ -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;

View File

@ -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() {

View File

@ -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();

View File

@ -114,6 +114,16 @@
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QProgressBar" name="m_progressBar">
<property name="enabled">
<bool>false</bool>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@ -282,7 +282,9 @@ void StandardFeed::fetchMetadataForItself() {
}
}
QPair<StandardFeed*,QNetworkReply::NetworkError> StandardFeed::guessFeed(const QString &url, const QString &username, const QString &password) {
QPair<StandardFeed*,QNetworkReply::NetworkError> StandardFeed::guessFeed(const QString &url,
const QString &username,
const QString &password) {
QPair<StandardFeed*,QNetworkReply::NetworkError> result; result.first = NULL;
QByteArray feed_contents;

View File

@ -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<StandardFeed*,QNetworkReply::NetworkError> guessFeed(const QString &url, const QString &username, const QString &password);
static QPair<StandardFeed*,QNetworkReply::NetworkError> guessFeed(const QString &url,
const QString &username = QString(),
const QString &password = QString());
// Converts particular feed type to string.
static QString typeToString(Type type);

View File

@ -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<RootItem*> model_items; model_items.push(root_item);
QStack<QDomElement> 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<QByteArray> urls = data.split('\n');
foreach (const QByteArray &url, data.split('\n')) {
foreach (const QByteArray &url, urls) {
if (!url.isEmpty()) {
QPair<StandardFeed*,QNetworkReply::NetworkError> 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 {

View File

@ -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<RootItem*, Qt::CheckState> m_checkStates;
QHash<RootItem*,Qt::CheckState> m_checkStates;
RootItem *m_rootItem;
bool m_recursiveChange;