Mercurial > clickerconvert
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 } |