27 #include "CSVDialog.rcd" 31 #include "dimension.h" 44 using ecolab::cairo::CairoSave;
46 #include <boost/filesystem.hpp> 47 using boost::filesystem::file_size;
49 const unsigned CSVDialog::numInitialLines;
51 void CSVDialog::reportFromFile(
const std::string&
input,
const std::string& output)
const 66 CacheEntry(
const string& url): timestamp(chrono::system_clock::now()), url(url),
67 filename(
boost::filesystem::unique_path().string()) {}
73 struct Cache:
private set<CacheEntry>
75 using set<CacheEntry>::find;
76 using set<CacheEntry>::end;
77 using set<CacheEntry>::erase;
83 auto entryToErase=begin();
84 auto ts=entryToErase->timestamp;
85 for (
auto i=begin(); i!=end(); ++i)
93 return set<CacheEntry>::emplace(url).first;
98 void CSVDialog::loadFile()
100 loadFileFromName(url);
103 void CSVDialog::guessSpecAndLoadFile()
106 spec.guessFromFile(url);
107 loadFileFromName(url);
112 void CSVDialog::loadFileFromName(
const std::string& fname)
116 initialLines.clear();
117 for (
size_t i=0; i<numInitialLines && is; ++i)
119 initialLines.emplace_back();
120 getline(is, initialLines.back());
122 if (!initialLines.back().empty() && initialLines.back().back()==
'\r')
123 initialLines.back().erase(initialLines.back().end()-1);
126 if (spec.dimensions.size()<spec.nColAxes()) spec.setDataArea(spec.nRowAxes(),spec.nColAxes());
129 template <
class Parser>
132 vector<vector<string>> r;
133 for (
const auto& line: lines)
138 const boost::tokenizer<Parser> tok(line.begin(), line.end(),
parser);
140 for (
size_t i=0; i<maxColumn && t!=tok.end(); ++i, ++t)
141 r.back().push_back(*t);
145 r.back().push_back(line);
157 CroppedPango(cairo_t* cairo,
double width): Pango(cairo), cairo(cairo), w(width) {}
158 void setxy(
double xx,
double yy) {x=xx; y=yy;}
160 const CairoSave cs(cairo);
161 cairo_rectangle(cairo,x,y,w,height());
163 cairo_move_to(cairo,x,y);
169 bool CSVDialog::redraw(
int,
int,
int,
int)
171 cairo_t* cairo=surface->cairo();
173 vector<vector<string>> parsedLines=
parseLines();
178 pango.setText(
"Dimension");
179 cairo_move_to(cairo,xoffs-pango.width()-5,0);
181 pango.setText(
"Type");
182 cairo_move_to(cairo,xoffs-pango.width()-5,rowHeight);
184 pango.setText(
"Format");
185 cairo_move_to(cairo,xoffs-pango.width()-5,2*rowHeight);
188 pango.setMarkup(
"<b>Name</b>");
190 pango.setText(
"Name");
191 cairo_move_to(cairo,xoffs-pango.width()-5,3*rowHeight);
193 pango.setText(
"Header");
194 cairo_move_to(cairo,xoffs-pango.width()-5,(4+spec.headerRow)*rowHeight);
199 CroppedPango pango(cairo, colWidth);
200 pango.setFontSize(0.8*rowHeight);
205 for (; done.size()<parsedLines.size(); ++col)
207 if (col<spec.nColAxes())
209 const CairoSave cs(cairo);
211 cairo_set_line_width(cairo,1);
212 cairo_translate(cairo,x+0.5*colWidth,y+0.5*rowHeight);
213 cairo_rectangle(cairo,-cbsz,-cbsz,2*cbsz,2*cbsz);
214 if (spec.dimensionCols.contains(col))
216 cairo_move_to(cairo,-cbsz,-cbsz);
217 cairo_line_to(cairo,cbsz,cbsz);
218 cairo_move_to(cairo,cbsz,-cbsz);
219 cairo_line_to(cairo,-cbsz,cbsz);
225 if (spec.dimensionCols.contains(col) && col<spec.dimensions.size() && col<spec.nColAxes())
227 pango.setText(classdesc::enumKey<Dimension::Type>(spec.dimensions[col].type));
232 if (spec.dimensionCols.contains(col) && col<spec.dimensions.size() && col<spec.nColAxes())
234 pango.setText(spec.dimensions[col].units);
239 if (spec.dimensionCols.contains(col) && col<spec.dimensionNames.size() && col<spec.nColAxes())
241 pango.setText(spec.dimensionNames[col]);
246 for (
size_t row=0; row<parsedLines.size(); ++row)
248 auto& line=parsedLines[row];
251 const CairoSave cs(cairo);
252 pango.setText(line[col]);
254 if (row==spec.headerRow)
255 if (col<spec.nColAxes())
256 cairo_set_source_rgb(surface->cairo(),0,0.7,0);
258 cairo_set_source_rgb(surface->cairo(),0,0,1);
259 else if (row<spec.nRowAxes() || (col<spec.nColAxes() && !spec.dimensionCols.contains(col)))
260 cairo_set_source_rgb(surface->cairo(),1,0,0);
261 else if (col<spec.nColAxes())
262 cairo_set_source_rgb(surface->cairo(),0,0,1);
270 const CairoSave cs(cairo);
271 cairo_set_source_rgb(cairo,.5,.5,.5);
272 cairo_move_to(cairo,x-2.5,0);
273 cairo_rel_line_to(cairo,0,(parsedLines.size()+4)*rowHeight);
279 m_tableWidth=(col-1)*(colWidth+5);
280 for (
size_t row=0; row<parsedLines.size()+5; ++row)
282 const CairoSave cs(cairo);
283 cairo_set_source_rgb(cairo,.5,.5,.5);
284 cairo_move_to(cairo,xoffs-2.5,row*rowHeight);
285 cairo_rel_line_to(cairo,m_tableWidth,0);
291 size_t CSVDialog::columnOver(
double x)
const 293 return size_t((x-xoffs)/(colWidth+5));
296 size_t CSVDialog::rowOver(
double y)
const 298 return size_t(y/rowHeight);
303 vector<vector<string>> parsedLines;
304 if (spec.mergeDelimiters)
305 if (spec.separator==
' ')
306 parsedLines=
::parseLines(boost::char_separator<char>(), initialLines, maxColumn);
309 char separators[]={spec.separator,
'\0'};
311 (boost::char_separator<char>(separators,
""),initialLines, maxColumn);
315 (boost::escaped_list_separator<char>(spec.escape,spec.separator,spec.quote),
316 initialLines, maxColumn);
319 if (maxColumn==numeric_limits<size_t>::max())
322 for (
auto& i: parsedLines)
323 spec.numCols=std::max(spec.numCols, i.size());
328 void CSVDialog::populateHeaders()
331 if (spec.headerRow>=parsedLines.size())
return;
332 auto& hr=parsedLines[spec.headerRow];
333 spec.dimensionNames={hr.begin(), min(hr.end(), hr.begin()+spec.maxColumn)};
336 void CSVDialog::populateHeader(
size_t col)
339 if (spec.headerRow>=parsedLines.size())
return;
340 auto& headers=parsedLines[spec.headerRow];
341 if (col<headers.size() && col<spec.maxColumn)
342 spec.dimensionNames[col]=headers[col];
345 void CSVDialog::classifyColumns()
348 spec.dimensionCols.clear();
349 spec.dataCols.clear();
350 spec.dimensions.resize(min(spec.numCols,spec.maxColumn));
351 for (
size_t col=0; col<spec.numCols; ++col)
353 bool entryFound=
false, timeFound=
true, numberFound=
true;
354 for (
size_t row=spec.nRowAxes(); row<parsedLines.size(); ++row)
355 if (col<parsedLines[row].size() && !parsedLines[row][col].empty())
358 if (numberFound && !
isNumerical(parsedLines[row][col]))
360 static const AnyVal any(Dimension(Dimension::time,
""));
363 {any(parsedLines[row][col]);}
367 if (entryFound && col<spec.maxColumn)
370 spec.dataCols.insert(col);
373 spec.dimensionCols.insert(col);
375 spec.dimensions[col].type=Dimension::time;
377 spec.dimensions[col].type=Dimension::string;
378 spec.dimensions[col].units.clear();
381 else if (col>=spec.nColAxes() && col<spec.maxColumn)
382 spec.dataCols.insert(col);
386 std::vector<size_t> CSVDialog::correctedUniqueValues()
388 auto r=spec.uniqueValues();
390 vector<set<size_t>> correction(r.size());
392 const hash<string> h;
393 for (
size_t row=0; row<parsedLines.size() && row<spec.nRowAxes(); ++row)
394 for (
size_t col=0; col<correction.size() && col<parsedLines[row].size(); ++col)
395 correction[col].insert(h(parsedLines[row][col]));
396 for (
size_t i=0; i<r.size(); ++i)
397 if (r[i]>correction[i].size())
398 r[i]-=correction[i].size();
void reportFromCSVFile(istream &input, ostream &output, const DataSpec &spec, uintmax_t fileSize)
creates a report CSV file from input, with errors sorted at begining of file, with a column for error...
CacheEntry(const string &url)
iterator emplace(const string &url)
CroppedPango(cairo_t *cairo, double width)
void setxy(double xx, double yy)
bool isNumerical(const std::string &s)
void remove(std::vector< T > &x, const V &v)
remove an element from a vector. V must be comparable to a T
CLASSDESC_ACCESS_EXPLICIT_INSTANTIATION(minsky::CSVDialog)
chrono::time_point< chrono::system_clock > timestamp
exprtk::parser< double > parser
vector< vector< string > > parseLines(const Parser &parser, const vector< string > &lines, size_t maxColumn)
bool operator<(const CacheEntry &x) const
void stripByteOrderingMarker(std::istream &s)
checks if the input stream has the UTF-8 byte ordering marker, and removes it if present ...