view src/converter.cpp @ 3:8b4c49c92451

Add initial implementation that handles choices
author Andre Heinecke <andre.heinecke@intevation.de>
date Tue, 22 Mar 2016 10:39:19 +0100
parents
children a10425e7ef98
line wrap: on
line source
/* Copyright (C) 2016 by ETH Zürich
 * Software engineering by Intevation GmbH
 *
 * This file is Free Software under the GNU GPL (v>=2)
 * and comes with ABSOLUTELY NO WARRANTY!
 * See LICENSE.txt for details.
 */

#include "converter.h"
#include <QDebug>
#include <QRegularExpression>
#include <QRegularExpressionMatch>

#include "xlsxdocument.h"
#include "xlsxconditionalformatting.h"

#include "constants.h"

QTXLSX_USE_NAMESPACE

Converter::Converter(const QString &input, const QString &output,
                     ConvertFormat fmt, const QString &title):
    QThread(Q_NULLPTR),
    mInput(input),
    mOutput(output),
    mFmt(fmt),
    mTitle(title)
{
    mTitleFmt.setFontUnderline(Format::FontUnderlineSingle);
    mTitleFmt.setFontSize(18);
    mTitleFmt.setFontName("Calibri");
    mTitleFmt.setFontBold(true);
    mTitleFmt.setVerticalAlignment(Format::AlignTop);

    mQuestionFmt.setFontSize(11);
    mQuestionFmt.setFontName("Calibri");
    mQuestionFmt.setFontBold(true);
    mQuestionFmt.setTopBorderStyle(Format::BorderThin);
    mQuestionFmt.setTextWarp(true);

    mAnswerChoiceFmt.setFontSize(11);
    mAnswerChoiceFmt.setFontName("Calibri");
    mAnswerChoiceFmt.setHorizontalAlignment(Format::AlignRight);

    mChoiceTextFmt = mAnswerChoiceFmt;
    mChoiceTextFmt.setVerticalAlignment(Format::AlignVCenter);

    mChoiceBarFmt = mChoiceTextFmt;
    mChoiceBarFmt.setFontName("Webdings");
    mChoiceBarFmt.setFontSize(9);
    mChoiceBarFmt.setFontColor(QColor(0xFF, 0x99, 0x33));

    mChoiceBarInactiveFmt = mChoiceBarFmt;
    mChoiceBarInactiveFmt.setFontColor(QColor(0xD9, 0xD9, 0xD9));
}

void Converter::run()
{
    QFile infile;

    if (mInput.isEmpty()) {
        if (!infile.open(stdin, QIODevice::ReadOnly)) {
            mErrors << tr("Failed to open standard input and no input file provided.");
            return;
        }
    } else {
        infile.setFileName(mInput);
        if (!infile.open(QIODevice::ReadOnly)) {
            mErrors << tr("Failed to open %1 for reading.").arg(mInput);
            return;
        }
    }
    QTextStream instream(&infile);

    QFile outfile;
    if (mOutput.isEmpty()) {
        if (!outfile.open(stdout, QIODevice::WriteOnly)) {
            mErrors << tr("Failed to open standard output and no output file provided.");
            return;
        }
    } else {
        outfile.setFileName(mOutput);
        if (!outfile.open(QIODevice::WriteOnly)) {
            mErrors << tr("Failed to open %1 for writing.").arg(mOutput);
            return;
        }
    }
    convertToXSLX(instream, outfile);
}

void Converter::convertToXSLX(QTextStream& instream, QFile &output)
{
    Document xlsx;

    ConditionalFormatting bars;

    bars.addDataBarRule(QColor(0xFF, 0x99, 0x33), ConditionalFormatting::VOT_Num, "0", ConditionalFormatting::VOT_Num, "100", false);

    const double colWidth[] = COLUMN_WIDTHS;
    for (int i = 1; i <= COLUMN_CNT; i++) {
        xlsx.setColumnWidth(i, colWidth[i-1]);
    }

    int row = 1;
    if (!mTitle.isEmpty()) {
        // Set the title of the Questionaire
        xlsx.write(row++, 1, mTitle, mTitleFmt);
        xlsx.mergeCells("A1:C1");
        xlsx.setRowHeight(1, TITLE_ROW_HEIGHT);
    }

    const QString input = instream.readAll();

    QRegularExpression questionEx(QUESTION_PATTERN);
    QRegularExpression choiceEx(CHOICE_PATTERN);
    QRegularExpression freetxtEx (FREETXT_PATTERN);

    QRegularExpressionMatch match = questionEx.match(input);
    bool foundSomething = false;
    int cursor = match.capturedEnd();
    while (match.hasMatch() && cursor != -1) {
        /* We've matched a question pattern. With the answer
           line */
        if (!match.lastCapturedIndex() == 2) {
            /* Should not happen without misconfiguration. */
            mErrors << "Internal parser error.";
            return;
        }
        foundSomething = true;
        const QString question = match.captured(1).trimmed();
        const QString answerLine = match.captured(2).trimmed();
        xlsx.write(row++, 1, question, mQuestionFmt);

        if (answerLine == QStringLiteral(CHOICE_IDENTIFIER)) {
            QRegularExpressionMatch choiceMatch = choiceEx.match(input, cursor);
            xlsx.setRowHeight(row, CHOICE_ROW_HEIGHT);
            xlsx.write(row++, 1, tr("Answer"), mAnswerChoiceFmt);
            int firstChoiceRow = row;
            int lastChoiceRow = row;
            while (choiceMatch.hasMatch() && choiceMatch.capturedStart() == cursor + 1) {
                /* We use the cursor here to keep track of the state. Only if an answer
                   follows immediately behind the last answer we treat it as valid as
                   otherwise we can't figure out when the next question begins. */
                cursor = choiceMatch.capturedEnd();

                /* Write the values */
                xlsx.write(row, 1, choiceMatch.captured(1), mChoiceTextFmt);
                bool ok;
                double percent = choiceMatch.captured(3).toDouble(&ok);
                if (!ok) {
                    mErrors << "Unparsable number in string: " + choiceMatch.captured();
                }
                xlsx.write(row, 2, percent);
                xlsx.write(row, 3, tr("%1% | %2 Number of votes").
                           arg(choiceMatch.captured(3)).arg(choiceMatch.captured(2)),
                           mChoiceVotesFmt);
                xlsx.setRowHeight(row, CHOICE_ROW_HEIGHT);
                /* As long as we can match a choice which is either before the next question
                   or before the end of the document */
                choiceMatch = choiceEx.match(input, cursor);
                row++;
                lastChoiceRow++;
            }
            bars.addRange(QString("B%1:B%2").arg(firstChoiceRow).arg(lastChoiceRow));
        } else if (answerLine == QStringLiteral(TEXT_IDENTIFIER)) {
            
        }
        /* Insert Empty row. */
        xlsx.setRowHeight(row++, CHOICE_ROW_HEIGHT);
        match = questionEx.match(input, cursor);
        cursor = match.capturedEnd();
    }
    xlsx.addConditionalFormatting(bars);

    if (!foundSomething) {
        mErrors << tr("Failed to parse input document.");
    }

    if (!xlsx.saveAs(&output)) {
        mErrors << tr("Saving the XLSX document failed.");
        return;
    }
}
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)