##// END OF EJS Templates
Handles "Not found" error for AMDA result parser
Alexandre Leroux -
r446:f2dd25b3cc0a
parent child
Show More
@@ -0,0 +1,3
1 Not Found
2
3 The requested URL /AMDA/data/WSRESULT/imf(0)-1343153090-1343153092-60.txt was not found on this server. No newline at end of file
@@ -1,134 +1,145
1 1 #include "AmdaResultParser.h"
2 2
3 3 #include <Data/ScalarSeries.h>
4 4
5 5 #include <QDateTime>
6 6 #include <QFile>
7 7 #include <QRegularExpression>
8 8
9 9 #include <cmath>
10 10
11 11 Q_LOGGING_CATEGORY(LOG_AmdaResultParser, "AmdaResultParser")
12 12
13 13 namespace {
14 14
15 /// Message in result file when the file was not found on server
16 const auto FILE_NOT_FOUND_MESSAGE = QStringLiteral("Not Found");
17
15 18 /// Format for dates in result files
16 19 const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz");
17 20
18 21 /// Separator between values in a result line
19 22 const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")};
20 23
21 24 /// Regex to find unit in a line. Examples of valid lines:
22 25 /// ... - Units : nT - ...
23 26 /// ... -Units:nT- ...
24 27 /// ... -Units: m²- ...
25 28 /// ... - Units : m/s - ...
26 29 const auto UNIT_REGEX = QRegularExpression{QStringLiteral("-\\s*Units\\s*:\\s*(.+?)\\s*-")};
27 30
28 31 /// Converts a string date to a double date
29 32 /// @return a double that represents the date in seconds, NaN if the string date can't be converted
30 33 double doubleDate(const QString &stringDate) noexcept
31 34 {
32 35 auto dateTime = QDateTime::fromString(stringDate, DATE_FORMAT);
33 36 return dateTime.isValid() ? (dateTime.toMSecsSinceEpoch() / 1000.)
34 37 : std::numeric_limits<double>::quiet_NaN();
35 38 }
36 39
37 40 /**
38 41 * Reads stream to retrieve x-axis unit
39 42 * @param stream the stream to read
40 43 * @return the unit that has been read in the stream, a default unit (time unit with no label) if an
41 44 * error occured during reading
42 45 */
43 46 Unit readXAxisUnit(QTextStream &stream)
44 47 {
45 48 QString line{};
46 49
47 50 if (stream.readLineInto(&line)) {
48 51 auto match = UNIT_REGEX.match(line);
49 52 if (match.hasMatch()) {
50 53 return Unit{match.captured(1), true};
51 54 }
52 55 else {
53 56 qCWarning(LOG_AmdaResultParser())
54 57 << QObject::tr("Can't read unit: invalid line %1").arg(line);
55 58 }
56 59 }
57 60 else {
58 61 qCWarning(LOG_AmdaResultParser()) << QObject::tr("Can't read unit: end of file");
59 62 }
60 63
61 64 // Error cases
62 65 return Unit{{}, true};
63 66 }
64 67
65 68 /**
66 69 * Reads stream to retrieve results
67 70 * @param stream the stream to read
68 71 * @return the pair of vectors x-axis data/values data that has been read in the stream
69 72 */
70 73 QPair<QVector<double>, QVector<double> > readResults(QTextStream &stream)
71 74 {
72 75 auto xData = QVector<double>{};
73 76 auto valuesData = QVector<double>{};
74 77
75 78 QString line{};
76 79 while (stream.readLineInto(&line)) {
77 80 auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
78 81 if (lineData.size() == 2) {
79 82 // X : the data is converted from date to double (in secs)
80 83 auto x = doubleDate(lineData.at(0));
81 84
82 85 // Value
83 86 bool valueOk;
84 87 auto value = lineData.at(1).toDouble(&valueOk);
85 88
86 89 // Adds result only if x and value are valid
87 90 if (!std::isnan(x) && !std::isnan(value) && valueOk) {
88 91 xData.push_back(x);
89 92 valuesData.push_back(value);
90 93 }
91 94 else {
92 95 qCWarning(LOG_AmdaResultParser())
93 96 << QObject::tr(
94 97 "Can't retrieve results from line %1: x and/or value are invalid")
95 98 .arg(line);
96 99 }
97 100 }
98 101 else {
99 102 qCWarning(LOG_AmdaResultParser())
100 103 << QObject::tr("Can't retrieve results from line %1: invalid line").arg(line);
101 104 }
102 105 }
103 106
104 107 return qMakePair(std::move(xData), std::move(valuesData));
105 108 }
106 109
107 110 } // namespace
108 111
109 112 std::shared_ptr<IDataSeries> AmdaResultParser::readTxt(const QString &filePath) noexcept
110 113 {
111 114 QFile file{filePath};
112 115
113 116 if (!file.open(QFile::ReadOnly | QIODevice::Text)) {
114 117 qCCritical(LOG_AmdaResultParser())
115 118 << QObject::tr("Can't retrieve AMDA data from file %1: %2")
116 119 .arg(filePath, file.errorString());
117 120 return nullptr;
118 121 }
119 122
120 123 QTextStream stream{&file};
121 124
122 // Ignore first two lines (comments lines)
123 stream.readLine();
125 // Checks if the file was found on the server
126 auto firstLine = stream.readLine();
127 if (firstLine.compare(FILE_NOT_FOUND_MESSAGE) == 0) {
128 qCCritical(LOG_AmdaResultParser())
129 << QObject::tr("Can't retrieve AMDA data from file %1: file was not found on server")
130 .arg(filePath);
131 return nullptr;
132 }
133
134 // Ignore comments lines
124 135 stream.readLine();
125 136
126 137 // Reads x-axis unit
127 138 auto xAxisUnit = readXAxisUnit(stream);
128 139
129 140 // Reads results
130 141 auto results = readResults(stream);
131 142
132 143 return std::make_shared<ScalarSeries>(std::move(results.first), std::move(results.second),
133 144 xAxisUnit, Unit{});
134 145 }
@@ -1,179 +1,182
1 1 #include "AmdaResultParser.h"
2 2
3 3 #include <Data/ScalarSeries.h>
4 4
5 5 #include <QObject>
6 6 #include <QtTest>
7 7
8 8 namespace {
9 9
10 10 /// Path for the tests
11 11 const auto TESTS_RESOURCES_PATH
12 12 = QFileInfo{QString{AMDA_TESTS_RESOURCES_DIR}, "TestAmdaResultParser"}.absoluteFilePath();
13 13
14 14 QString inputFilePath(const QString &inputFileName)
15 15 {
16 16 return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath();
17 17 }
18 18
19 19 struct ExpectedResults {
20 20 explicit ExpectedResults() = default;
21 21
22 22 /// Ctor with QVector<QDateTime> as x-axis data. Datetimes are converted to doubles
23 23 explicit ExpectedResults(Unit xAxisUnit, Unit valuesUnit, const QVector<QDateTime> &xAxisData,
24 24 QVector<double> valuesData)
25 25 : m_ParsingOK{true},
26 26 m_XAxisUnit{xAxisUnit},
27 27 m_ValuesUnit{valuesUnit},
28 28 m_XAxisData{},
29 29 m_ValuesData{std::move(valuesData)}
30 30 {
31 31 // Converts QVector<QDateTime> to QVector<double>
32 32 std::transform(xAxisData.cbegin(), xAxisData.cend(), std::back_inserter(m_XAxisData),
33 33 [](const auto &dateTime) { return dateTime.toMSecsSinceEpoch() / 1000.; });
34 34 }
35 35
36 36 /**
37 37 * Validates a DataSeries compared to the expected results
38 38 * @param results the DataSeries to validate
39 39 */
40 40 void validate(std::shared_ptr<IDataSeries> results)
41 41 {
42 42 if (m_ParsingOK) {
43 43 auto scalarSeries = dynamic_cast<ScalarSeries *>(results.get());
44 44 QVERIFY(scalarSeries != nullptr);
45 45
46 46 // Checks units
47 47 QVERIFY(scalarSeries->xAxisUnit() == m_XAxisUnit);
48 48 QVERIFY(scalarSeries->valuesUnit() == m_ValuesUnit);
49 49
50 50 // Checks values
51 51 QVERIFY(scalarSeries->xAxisData()->data() == m_XAxisData);
52 52 QVERIFY(scalarSeries->valuesData()->data() == m_ValuesData);
53 53 }
54 54 else {
55 55 QVERIFY(results == nullptr);
56 56 }
57 57 }
58 58
59 59 // Parsing was successfully completed
60 60 bool m_ParsingOK{false};
61 61 // Expected x-axis unit
62 62 Unit m_XAxisUnit{};
63 63 // Expected values unit
64 64 Unit m_ValuesUnit{};
65 65 // Expected x-axis data
66 66 QVector<double> m_XAxisData{};
67 67 // Expected values data
68 68 QVector<double> m_ValuesData{};
69 69 };
70 70
71 71 } // namespace
72 72
73 73 Q_DECLARE_METATYPE(ExpectedResults)
74 74
75 75 class TestAmdaResultParser : public QObject {
76 76 Q_OBJECT
77 77 private slots:
78 78 /// Input test data
79 79 /// @sa testTxtJson()
80 80 void testReadTxt_data();
81 81
82 82 /// Tests parsing of a TXT file
83 83 void testReadTxt();
84 84 };
85 85
86 86 void TestAmdaResultParser::testReadTxt_data()
87 87 {
88 88 // ////////////// //
89 89 // Test structure //
90 90 // ////////////// //
91 91
92 92 // Name of TXT file to read
93 93 QTest::addColumn<QString>("inputFileName");
94 94 // Expected results
95 95 QTest::addColumn<ExpectedResults>("expectedResults");
96 96
97 97 // ////////// //
98 98 // Test cases //
99 99 // ////////// //
100 100
101 101 auto dateTime = [](int year, int month, int day, int hours, int minutes, int seconds) {
102 102 return QDateTime{{year, month, day}, {hours, minutes, seconds}};
103 103 };
104 104
105 105 // Valid file
106 106 QTest::newRow("Valid file")
107 107 << QStringLiteral("ValidScalar1.txt")
108 108 << ExpectedResults{
109 109 Unit{QStringLiteral("nT"), true}, Unit{},
110 110 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
111 111 dateTime(2013, 9, 23, 9, 2, 30), dateTime(2013, 9, 23, 9, 3, 30),
112 112 dateTime(2013, 9, 23, 9, 4, 30), dateTime(2013, 9, 23, 9, 5, 30),
113 113 dateTime(2013, 9, 23, 9, 6, 30), dateTime(2013, 9, 23, 9, 7, 30),
114 114 dateTime(2013, 9, 23, 9, 8, 30), dateTime(2013, 9, 23, 9, 9, 30)},
115 115 QVector<double>{-2.83950, -2.71850, -2.52150, -2.57633, -2.58050, -2.48325, -2.63025,
116 116 -2.55800, -2.43250, -2.42200}};
117 117
118 118 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
119 119 QTest::newRow("No unit file") << QStringLiteral("NoUnit.txt")
120 120 << ExpectedResults{Unit{QStringLiteral(""), true}, Unit{},
121 121 QVector<QDateTime>{}, QVector<double>{}};
122 122 QTest::newRow("Wrong unit file")
123 123 << QStringLiteral("WrongUnit.txt")
124 124 << ExpectedResults{Unit{QStringLiteral(""), true}, Unit{},
125 125 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30),
126 126 dateTime(2013, 9, 23, 9, 1, 30),
127 127 dateTime(2013, 9, 23, 9, 2, 30)},
128 128 QVector<double>{-2.83950, -2.71850, -2.52150}};
129 129
130 130 QTest::newRow("Wrong results file (date of first line is invalid")
131 131 << QStringLiteral("WrongDate.txt")
132 132 << ExpectedResults{
133 133 Unit{QStringLiteral("nT"), true}, Unit{},
134 134 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
135 135 QVector<double>{-2.71850, -2.52150}};
136 136
137 137 QTest::newRow("Wrong results file (too many values for first line")
138 138 << QStringLiteral("TooManyValues.txt")
139 139 << ExpectedResults{
140 140 Unit{QStringLiteral("nT"), true}, Unit{},
141 141 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
142 142 QVector<double>{-2.71850, -2.52150}};
143 143
144 144 QTest::newRow("Wrong results file (value of first line is invalid")
145 145 << QStringLiteral("WrongValue.txt")
146 146 << ExpectedResults{
147 147 Unit{QStringLiteral("nT"), true}, Unit{},
148 148 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
149 149 QVector<double>{-2.71850, -2.52150}};
150 150
151 151 QTest::newRow("Wrong results file (value of first line is NaN")
152 152 << QStringLiteral("NaNValue.txt")
153 153 << ExpectedResults{
154 154 Unit{QStringLiteral("nT"), true}, Unit{},
155 155 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
156 156 QVector<double>{-2.71850, -2.52150}};
157 157
158 // Invalid file
159 QTest::newRow("Invalid file (unexisting file)") << QStringLiteral("UnexistingFile.txt")
160 << ExpectedResults{};
158 // Invalid files
159 QTest::newRow("Invalid file (unexisting file)")
160 << QStringLiteral("UnexistingFile.txt") << ExpectedResults{};
161
162 QTest::newRow("Invalid file (file not found on server)")
163 << QStringLiteral("FileNotFound.txt") << ExpectedResults{};
161 164 }
162 165
163 166 void TestAmdaResultParser::testReadTxt()
164 167 {
165 168 QFETCH(QString, inputFileName);
166 169 QFETCH(ExpectedResults, expectedResults);
167 170
168 171 // Parses file
169 172 auto filePath = inputFilePath(inputFileName);
170 173 auto results = AmdaResultParser::readTxt(filePath);
171 174
172 175 // ///////////////// //
173 176 // Validates results //
174 177 // ///////////////// //
175 178 expectedResults.validate(results);
176 179 }
177 180
178 181 QTEST_MAIN(TestAmdaResultParser)
179 182 #include "TestAmdaResultParser.moc"
General Comments 0
You need to be logged in to leave comments. Login now