File indexing completed on 2025-02-02 12:00:33

0001 /* This file is part of the KDE project
0002    SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
0003    SPDX-FileCopyrightText: 2004 Nicolas GOUTTE <goutte@kde.org>
0004 
0005    SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "csvimport.h"
0009 
0010 #include <QFile>
0011 #include <QGuiApplication>
0012 
0013 #include <kmessagebox.h>
0014 #include <kpluginfactory.h>
0015 
0016 #include <KoCsvImportDialog.h>
0017 #include <KoFilterChain.h>
0018 #include <KoFilterManager.h>
0019 
0020 #include <sheets/engine/ElapsedTime_p.h>
0021 #include <sheets/engine/CalculationSettings.h>
0022 #include <sheets/engine/Localization.h>
0023 #include <sheets/engine/Value.h>
0024 #include <sheets/engine/ValueConverter.h>
0025 #include <sheets/core/Cell.h>
0026 #include <sheets/core/ColFormatStorage.h>
0027 #include <sheets/core/DocBase.h>
0028 #include <sheets/core/Map.h>
0029 #include <sheets/core/Sheet.h>
0030 
0031 using namespace Calligra::Sheets;
0032 
0033 /*
0034  To generate a test CSV file:
0035 
0036  perl -e '$i=0;while($i<30000) { print rand().",".rand()."\n"; $i++ }' > file.csv
0037 */
0038 
0039 K_PLUGIN_FACTORY_WITH_JSON(CSVImportFactory, "calligra_filter_csv2sheets.json", registerPlugin<CSVFilter>();)
0040 
0041 Q_LOGGING_CATEGORY(lcCsvImport, "calligra.filter.csv.import")
0042 
0043 CSVFilter::CSVFilter(QObject* parent, const QVariantList&) :
0044         KoFilter(parent)
0045 {
0046 }
0047 
0048 KoFilter::ConversionStatus CSVFilter::convert(const QByteArray& from, const QByteArray& to)
0049 {
0050     QString file(m_chain->inputFile());
0051     KoDocument* document = m_chain->outputDocument();
0052 
0053     if (!document)
0054         return KoFilter::StupidError;
0055 
0056     if (!qobject_cast<const Calligra::Sheets::DocBase *>(document)) {
0057         qWarning(lcCsvImport) << "document isn't a Calligra::Sheets::Doc but a " << document->metaObject()->className();
0058         return KoFilter::NotImplemented;
0059     }
0060     if ((from != "text/csv" && from != "text/tab-separated-values" && from != "text/plain") || to != "application/vnd.oasis.opendocument.spreadsheet") {
0061         qWarning(lcCsvImport) << "Invalid mimetypes " << from << " " << to;
0062         return KoFilter::NotImplemented;
0063     }
0064 
0065     DocBase *ksdoc = dynamic_cast<DocBase *>(document);   // type checked above
0066 
0067 //    if (ksdoc->mimeType() != "application/vnd.oasis.opendocument.spreadsheet") {
0068 //       qWarning(lcCsvImport) << "Invalid document mimetype " << ksdoc->mimeType();
0069 //        return KoFilter::NotImplemented;
0070 //    }
0071 
0072     QFile in(file);
0073     if (!in.open(QIODevice::ReadOnly)) {
0074         KMessageBox::sorry(0L, i18n("CSV filter cannot open input file - please report."));
0075         in.close();
0076         return KoFilter::FileNotFound;
0077     }
0078 
0079     // ###### FIXME: disabled for now
0080     //QString csv_delimiter;
0081     //if (!config.isNull())
0082     //    csv_delimiter = config[0];
0083 
0084     QByteArray inputFile(in.readAll());
0085     in.close();
0086 
0087     Localization *locale = ksdoc->map()->calculationSettings()->locale();
0088     ValueConverter *conv = ksdoc->map()->converter();
0089 
0090     QString decimal = locale->decimalSymbol();
0091     QString thousands = locale->thousandsSeparator();
0092 
0093     KoCsvImportDialog* dialog = new KoCsvImportDialog(0);
0094     dialog->setData(inputFile);
0095     dialog->setDecimalSymbol(decimal);
0096     dialog->setThousandsSeparator(thousands);
0097     if (!m_chain->manager()->getBatchMode() && !dialog->exec())
0098         return KoFilter::UserCancelled;
0099     inputFile.resize(0);   // Release memory (input file content)
0100 
0101     ElapsedTime t("Filling data into document");
0102 
0103     Sheet *sheet = dynamic_cast<Sheet *>(ksdoc->map()->addNewSheet());
0104 
0105     int numRows = dialog->rows();
0106     int numCols = dialog->cols();
0107 
0108     if (numRows == 0)
0109         ++numRows;
0110 
0111     // Initialize the decimal symbol and thousands separator to use for parsing.
0112     decimal = dialog->decimalSymbol();
0113     thousands = dialog->thousandsSeparator();
0114 
0115     int value = 0;
0116 
0117     emit sigProgress(value);
0118     QGuiApplication::setOverrideCursor(Qt::WaitCursor);
0119 
0120     Cell cell(sheet, 1, 1);
0121 
0122     int processed = 0;
0123     for (int row = 0; row < numRows; ++row) {
0124         for (int col = 0; col < numCols; ++col) {
0125             processed++;
0126             int progress = (int) (100.0 * processed / (numRows * numCols));
0127             if (progress != value) {
0128                 value = progress;
0129                 emit sigProgress(value);
0130             }
0131 
0132             const QString text(dialog->text(row, col));
0133 
0134             cell = Cell(sheet, col + 1, row + 1);
0135 
0136             // TODO - try to format numbers
0137 
0138             switch (dialog->dataType(col)) {
0139             case KoCsvImportDialog::Generic:
0140             default: {
0141                 Value val;
0142                 // Is this a valid number?
0143                 QString numtext = text;
0144                 numtext = numtext.replace(' ', QString());
0145                 numtext = numtext.replace(thousands, QString());
0146                 numtext = numtext.replace(decimal, ".");
0147                 bool ok = false;
0148                 double num = numtext.toDouble(&ok);
0149                 if (ok) val = Value(num);
0150 
0151                 if (!val.isEmpty())
0152                     cell.setCellValue(val);
0153                 else
0154                     cell.parseUserInput(text);
0155                 break;
0156             }
0157             case KoCsvImportDialog::Text: {
0158                 Value value(text);
0159                 cell.setCellValue(value);
0160                 break;
0161             }
0162             case KoCsvImportDialog::Date: {
0163                 Value value(text);
0164                 cell.setCellValue(conv->asDate(value));
0165                 break;
0166             }
0167             case KoCsvImportDialog::Currency: {
0168                 Value value(text);
0169                 value = conv->asNumeric(value);
0170                 value.setFormat(Value::fmt_Money);
0171                 cell.setCellValue(value);
0172                 break;
0173             }
0174             case KoCsvImportDialog::None: {
0175                 // just skip the content
0176                 break;
0177             }
0178             }
0179         }
0180     }
0181 
0182     emit sigProgress(100);
0183     QGuiApplication::restoreOverrideCursor();
0184     delete dialog;
0185 
0186     return KoFilter::OK;
0187 }
0188 
0189 #include <csvimport.moc>