Mercurial > clickerconvert
diff 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 diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/converter.cpp Tue Mar 22 10:39:19 2016 +0100 @@ -0,0 +1,183 @@ +/* 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; + } +}