File indexing completed on 2025-09-14 03:42:58

0001 /*
0002     File                 : JsonOptionsWidget.cpp
0003     Project              : LabPlot
0004     Description          : Widget providing options for the import of json data.
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2018 Andrey Cygankov <craftplace.ms@gmail.com>
0007     SPDX-FileCopyrightText: 2018-2023 Alexander Semke <alexander.semke@web.de>
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include "JsonOptionsWidget.h"
0012 #include "ImportFileWidget.h"
0013 #include "backend/core/Settings.h"
0014 #include "backend/datasources/filters/AbstractFileFilter.h"
0015 #include "backend/datasources/filters/JsonFilter.h"
0016 #include "backend/datasources/filters/QJsonModel.h"
0017 #include "backend/lib/trace.h"
0018 
0019 #include <KCompressionDevice>
0020 #include <KConfigGroup>
0021 #include <KLocalizedString>
0022 
0023 /*!
0024 \class JsonOptionsWidget
0025 \brief Widget providing options for the import of json data
0026 
0027 \ingroup kdefrontend
0028 */
0029 JsonOptionsWidget::JsonOptionsWidget(QWidget* parent)
0030     : QWidget(parent)
0031     , m_model(new QJsonModel()) {
0032     ui.setupUi(parent);
0033 
0034     ui.cbDecimalSeparator->addItem(i18n("Point '.'"));
0035     ui.cbDecimalSeparator->addItem(i18n("Comma ','"));
0036     ui.cbDateTimeFormat->addItems(AbstractColumn::dateTimeFormats());
0037 
0038     connect(m_model, &QJsonModel::error, this, &JsonOptionsWidget::error);
0039 
0040     setTooltips();
0041 }
0042 
0043 void JsonOptionsWidget::applyFilterSettings(JsonFilter* filter, const QModelIndex& index) const {
0044     Q_ASSERT(filter);
0045 
0046     filter->setModel(m_model);
0047     filter->setModelRows(getIndexRows(index));
0048 
0049     QLocale::Language lang;
0050     if (ui.cbDecimalSeparator->currentIndex() == 0)
0051         lang = QLocale::Language::C;
0052     else
0053         lang = QLocale::Language::German;
0054     filter->setNumberFormat(lang);
0055 
0056     filter->setDateTimeFormat(ui.cbDateTimeFormat->currentText());
0057     filter->setCreateIndexEnabled(ui.chbCreateIndex->isChecked());
0058     filter->setNaNValueToZero(ui.chbConvertNaNToZero->isChecked());
0059     filter->setImportObjectNames(ui.chbImportObjectNames->isChecked());
0060 
0061     // TODO: change this after implementation other row types
0062     filter->setDataRowType(QJsonValue::Array);
0063     if (!index.isValid())
0064         return;
0065     auto* item = static_cast<QJsonTreeItem*>(index.internalPointer());
0066     if (item->childCount() < 1)
0067         return;
0068     filter->setDataRowType(item->child(0)->type());
0069 }
0070 
0071 void JsonOptionsWidget::clearModel() {
0072     m_model->clear();
0073     m_filename.clear();
0074 }
0075 
0076 void JsonOptionsWidget::loadSettings() const {
0077     KConfigGroup conf = Settings::group(QStringLiteral("ImportJson"));
0078 
0079     const auto decimalSeparator = QLocale().decimalPoint();
0080     int index = (decimalSeparator == QLatin1Char('.')) ? 0 : 1;
0081     ui.cbDecimalSeparator->setCurrentIndex(conf.readEntry("DecimalSeparator", index));
0082 
0083     ui.cbDateTimeFormat->setCurrentItem(conf.readEntry("DateTimeFormat", "yyyy-MM-dd hh:mm:ss.zzz"));
0084     ui.chbCreateIndex->setChecked(conf.readEntry("CreateIndex", false));
0085     ui.chbConvertNaNToZero->setChecked(conf.readEntry("ConvertNaNToZero", false));
0086     ui.chbImportObjectNames->setChecked(conf.readEntry("ParseRowsName", false));
0087 }
0088 
0089 void JsonOptionsWidget::saveSettings() {
0090     KConfigGroup conf = Settings::group(QStringLiteral("ImportJson"));
0091 
0092     conf.writeEntry("DecimalSeparator", ui.cbDecimalSeparator->currentIndex());
0093     conf.writeEntry("DateTimeFormat", ui.cbDateTimeFormat->currentText());
0094     conf.writeEntry("CreateIndex", ui.chbCreateIndex->isChecked());
0095     conf.writeEntry("ConvertNaNToZero", ui.chbConvertNaNToZero->isChecked());
0096     conf.writeEntry("ParseRowsName", ui.chbImportObjectNames->isChecked());
0097 }
0098 
0099 void JsonOptionsWidget::loadDocument(const QString& filename) {
0100     PERFTRACE(QStringLiteral("JsonOptionsWidget::loadDocument"));
0101     if (m_filename == filename)
0102         return;
0103     else
0104         m_filename = filename;
0105 
0106     KCompressionDevice device(m_filename);
0107     m_model->clear();
0108     if (!device.open(QIODevice::ReadOnly) || (device.atEnd() && !device.isSequential()) || // empty file
0109         !m_model->loadJson(device.readAll()))
0110         clearModel();
0111 }
0112 
0113 QAbstractItemModel* JsonOptionsWidget::model() {
0114     return m_model;
0115 }
0116 
0117 void JsonOptionsWidget::setTooltips() {
0118     const QString textNumberFormatShort = i18n("This option determines how the imported strings have to be converted to numbers.");
0119     const QString textNumberFormat = textNumberFormatShort + QStringLiteral("<br><br>")
0120         + i18n("For 'C Format', a period is used for the decimal point character and comma is used for the thousands group separator. "
0121                "Valid number representations are:"
0122                "<ul>"
0123                "<li>1234.56</li>"
0124                "<li>1,234.56</li>"
0125                "<li>etc.</li>"
0126                "</ul>"
0127                "When using 'System locale', the system settings will be used. "
0128                "E.g., for the German local the valid number representations are:"
0129                "<ul>"
0130                "<li>1234,56</li>"
0131                "<li>1.234,56</li>"
0132                "<li>etc.</li>"
0133                "</ul>");
0134 
0135     ui.lDecimalSeparator->setToolTip(textNumberFormatShort);
0136     ui.lDecimalSeparator->setWhatsThis(textNumberFormat);
0137     ui.cbDecimalSeparator->setToolTip(textNumberFormatShort);
0138     ui.cbDecimalSeparator->setWhatsThis(textNumberFormat);
0139 
0140     const QString textDateTimeFormatShort = i18n(
0141         "This option determines how the imported strings have to be converted to calendar date, i.e. year, month, and day numbers in the Gregorian calendar "
0142         "and to time.");
0143     const QString textDateTimeFormat = textDateTimeFormatShort + QStringLiteral("<br><br>")
0144         + i18n("Expressions that may be used for the date part of format string:"
0145                "<table>"
0146                "<tr><td>d</td><td>the day as number without a leading zero (1 to 31).</td></tr>"
0147                "<tr><td>dd</td><td>the day as number with a leading zero (01 to 31).</td></tr>"
0148                "<tr><td>ddd</td><td>the abbreviated localized day name (e.g. 'Mon' to 'Sun'). Uses the system locale to localize the name.</td></tr>"
0149                "<tr><td>dddd</td><td>the long localized day name (e.g. 'Monday' to 'Sunday'). Uses the system locale to localize the name.</td></tr>"
0150                "<tr><td>M</td><td>the month as number without a leading zero (1 to 12).</td></tr>"
0151                "<tr><td>MM</td><td>the month as number with a leading zero (01 to 12).</td></tr>"
0152                "<tr><td>MMM</td><td>the abbreviated localized month name (e.g. 'Jan' to 'Dec'). Uses the system locale to localize the name.</td></tr>"
0153                "<tr><td>MMMM</td><td>the long localized month name (e.g. 'January' to 'December'). Uses the system locale to localize the name.</td></tr>"
0154                "<tr><td>yy</td><td>the year as two digit number (00 to 99).</td></tr>"
0155                "<tr><td>yyyy</td><td>the year as four digit number. If the year is negative, a minus sign is prepended in addition.</td></tr>"
0156                "</table><br><br>"
0157                "Expressions that may be used for the time part of the format string:"
0158                "<table>"
0159                "<tr><td>h</td><td>the hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display)</td></tr>"
0160                "<tr><td>hh</td><td>the hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display)</td></tr>"
0161                "<tr><td>H</td><td>the hour without a leading zero (0 to 23, even with AM/PM display)</td></tr>"
0162                "<tr><td>HH</td><td>the hour with a leading zero (00 to 23, even with AM/PM display)</td></tr>"
0163                "<tr><td>m</td><td>the minute without a leading zero (0 to 59)</td></tr>"
0164                "<tr><td>mm</td><td>the minute with a leading zero (00 to 59)</td></tr>"
0165                "<tr><td>s</td><td>the second without a leading zero (0 to 59)</td></tr>"
0166                "<tr><td>ss</td><td>the second with a leading zero (00 to 59)</td></tr>"
0167                "<tr><td>z</td><td>the milliseconds without leading zeroes (0 to 999)</td></tr>"
0168                "<tr><td>zzz</td><td>the milliseconds with leading zeroes (000 to 999)</td></tr>"
0169                "<tr><td>AP or A</td><td>interpret as an AM/PM time. AP must be either 'AM' or 'PM'.</td></tr>"
0170                "<tr><td>ap or a</td><td>Interpret as an AM/PM time. ap must be either 'am' or 'pm'.</td></tr>"
0171                "</table><br><br>"
0172                "Examples are:"
0173                "<table>"
0174                "<tr><td>dd.MM.yyyy</td><td>20.07.1969</td></tr>"
0175                "<tr><td>ddd MMMM d yy</td><td>Sun July 20 69</td></tr>"
0176                "<tr><td>'The day is' dddd</td><td>The day is Sunday</td></tr>"
0177                "</table>");
0178 
0179     ui.lDateTimeFormat->setToolTip(textDateTimeFormatShort);
0180     ui.lDateTimeFormat->setWhatsThis(textDateTimeFormat);
0181     ui.cbDateTimeFormat->setToolTip(textDateTimeFormatShort);
0182     ui.cbDateTimeFormat->setWhatsThis(textDateTimeFormat);
0183 }
0184 
0185 QVector<int> JsonOptionsWidget::getIndexRows(const QModelIndex& index) const {
0186     QVector<int> rows;
0187     QModelIndex current = index;
0188     while (current.isValid()) {
0189         rows.prepend(current.row());
0190         current = current.parent();
0191     }
0192     return rows;
0193 }