comparison 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
comparison
equal deleted inserted replaced
2:4926d626fe15 3:8b4c49c92451
1 /* Copyright (C) 2016 by ETH Zürich
2 * Software engineering by Intevation GmbH
3 *
4 * This file is Free Software under the GNU GPL (v>=2)
5 * and comes with ABSOLUTELY NO WARRANTY!
6 * See LICENSE.txt for details.
7 */
8
9 #include "converter.h"
10 #include <QDebug>
11 #include <QRegularExpression>
12 #include <QRegularExpressionMatch>
13
14 #include "xlsxdocument.h"
15 #include "xlsxconditionalformatting.h"
16
17 #include "constants.h"
18
19 QTXLSX_USE_NAMESPACE
20
21 Converter::Converter(const QString &input, const QString &output,
22 ConvertFormat fmt, const QString &title):
23 QThread(Q_NULLPTR),
24 mInput(input),
25 mOutput(output),
26 mFmt(fmt),
27 mTitle(title)
28 {
29 mTitleFmt.setFontUnderline(Format::FontUnderlineSingle);
30 mTitleFmt.setFontSize(18);
31 mTitleFmt.setFontName("Calibri");
32 mTitleFmt.setFontBold(true);
33 mTitleFmt.setVerticalAlignment(Format::AlignTop);
34
35 mQuestionFmt.setFontSize(11);
36 mQuestionFmt.setFontName("Calibri");
37 mQuestionFmt.setFontBold(true);
38 mQuestionFmt.setTopBorderStyle(Format::BorderThin);
39 mQuestionFmt.setTextWarp(true);
40
41 mAnswerChoiceFmt.setFontSize(11);
42 mAnswerChoiceFmt.setFontName("Calibri");
43 mAnswerChoiceFmt.setHorizontalAlignment(Format::AlignRight);
44
45 mChoiceTextFmt = mAnswerChoiceFmt;
46 mChoiceTextFmt.setVerticalAlignment(Format::AlignVCenter);
47
48 mChoiceBarFmt = mChoiceTextFmt;
49 mChoiceBarFmt.setFontName("Webdings");
50 mChoiceBarFmt.setFontSize(9);
51 mChoiceBarFmt.setFontColor(QColor(0xFF, 0x99, 0x33));
52
53 mChoiceBarInactiveFmt = mChoiceBarFmt;
54 mChoiceBarInactiveFmt.setFontColor(QColor(0xD9, 0xD9, 0xD9));
55 }
56
57 void Converter::run()
58 {
59 QFile infile;
60
61 if (mInput.isEmpty()) {
62 if (!infile.open(stdin, QIODevice::ReadOnly)) {
63 mErrors << tr("Failed to open standard input and no input file provided.");
64 return;
65 }
66 } else {
67 infile.setFileName(mInput);
68 if (!infile.open(QIODevice::ReadOnly)) {
69 mErrors << tr("Failed to open %1 for reading.").arg(mInput);
70 return;
71 }
72 }
73 QTextStream instream(&infile);
74
75 QFile outfile;
76 if (mOutput.isEmpty()) {
77 if (!outfile.open(stdout, QIODevice::WriteOnly)) {
78 mErrors << tr("Failed to open standard output and no output file provided.");
79 return;
80 }
81 } else {
82 outfile.setFileName(mOutput);
83 if (!outfile.open(QIODevice::WriteOnly)) {
84 mErrors << tr("Failed to open %1 for writing.").arg(mOutput);
85 return;
86 }
87 }
88 convertToXSLX(instream, outfile);
89 }
90
91 void Converter::convertToXSLX(QTextStream& instream, QFile &output)
92 {
93 Document xlsx;
94
95 ConditionalFormatting bars;
96
97 bars.addDataBarRule(QColor(0xFF, 0x99, 0x33), ConditionalFormatting::VOT_Num, "0", ConditionalFormatting::VOT_Num, "100", false);
98
99 const double colWidth[] = COLUMN_WIDTHS;
100 for (int i = 1; i <= COLUMN_CNT; i++) {
101 xlsx.setColumnWidth(i, colWidth[i-1]);
102 }
103
104 int row = 1;
105 if (!mTitle.isEmpty()) {
106 // Set the title of the Questionaire
107 xlsx.write(row++, 1, mTitle, mTitleFmt);
108 xlsx.mergeCells("A1:C1");
109 xlsx.setRowHeight(1, TITLE_ROW_HEIGHT);
110 }
111
112 const QString input = instream.readAll();
113
114 QRegularExpression questionEx(QUESTION_PATTERN);
115 QRegularExpression choiceEx(CHOICE_PATTERN);
116 QRegularExpression freetxtEx (FREETXT_PATTERN);
117
118 QRegularExpressionMatch match = questionEx.match(input);
119 bool foundSomething = false;
120 int cursor = match.capturedEnd();
121 while (match.hasMatch() && cursor != -1) {
122 /* We've matched a question pattern. With the answer
123 line */
124 if (!match.lastCapturedIndex() == 2) {
125 /* Should not happen without misconfiguration. */
126 mErrors << "Internal parser error.";
127 return;
128 }
129 foundSomething = true;
130 const QString question = match.captured(1).trimmed();
131 const QString answerLine = match.captured(2).trimmed();
132 xlsx.write(row++, 1, question, mQuestionFmt);
133
134 if (answerLine == QStringLiteral(CHOICE_IDENTIFIER)) {
135 QRegularExpressionMatch choiceMatch = choiceEx.match(input, cursor);
136 xlsx.setRowHeight(row, CHOICE_ROW_HEIGHT);
137 xlsx.write(row++, 1, tr("Answer"), mAnswerChoiceFmt);
138 int firstChoiceRow = row;
139 int lastChoiceRow = row;
140 while (choiceMatch.hasMatch() && choiceMatch.capturedStart() == cursor + 1) {
141 /* We use the cursor here to keep track of the state. Only if an answer
142 follows immediately behind the last answer we treat it as valid as
143 otherwise we can't figure out when the next question begins. */
144 cursor = choiceMatch.capturedEnd();
145
146 /* Write the values */
147 xlsx.write(row, 1, choiceMatch.captured(1), mChoiceTextFmt);
148 bool ok;
149 double percent = choiceMatch.captured(3).toDouble(&ok);
150 if (!ok) {
151 mErrors << "Unparsable number in string: " + choiceMatch.captured();
152 }
153 xlsx.write(row, 2, percent);
154 xlsx.write(row, 3, tr("%1% | %2 Number of votes").
155 arg(choiceMatch.captured(3)).arg(choiceMatch.captured(2)),
156 mChoiceVotesFmt);
157 xlsx.setRowHeight(row, CHOICE_ROW_HEIGHT);
158 /* As long as we can match a choice which is either before the next question
159 or before the end of the document */
160 choiceMatch = choiceEx.match(input, cursor);
161 row++;
162 lastChoiceRow++;
163 }
164 bars.addRange(QString("B%1:B%2").arg(firstChoiceRow).arg(lastChoiceRow));
165 } else if (answerLine == QStringLiteral(TEXT_IDENTIFIER)) {
166
167 }
168 /* Insert Empty row. */
169 xlsx.setRowHeight(row++, CHOICE_ROW_HEIGHT);
170 match = questionEx.match(input, cursor);
171 cursor = match.capturedEnd();
172 }
173 xlsx.addConditionalFormatting(bars);
174
175 if (!foundSomething) {
176 mErrors << tr("Failed to parse input document.");
177 }
178
179 if (!xlsx.saveAs(&output)) {
180 mErrors << tr("Saving the XLSX document failed.");
181 return;
182 }
183 }
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)