27 #include "CSVDialog.rcd" 30 #include "dimension.h" 43 using ecolab::cairo::CairoSave;
45 #include <boost/filesystem.hpp> 46 using boost::filesystem::file_size;
48 const unsigned CSVDialog::numInitialLines;
50 void CSVDialog::reportFromFile(
const std::string&
input,
const std::string& output)
const 65 CacheEntry(
const string& url): timestamp(chrono::system_clock::now()), url(url),
66 filename(
boost::filesystem::unique_path().string()) {}
72 struct Cache:
private set<CacheEntry>
74 using set<CacheEntry>::find;
75 using set<CacheEntry>::end;
76 using set<CacheEntry>::erase;
82 auto entryToErase=begin();
83 auto ts=entryToErase->timestamp;
84 for (
auto i=begin(); i!=end(); ++i)
92 return set<CacheEntry>::emplace(url).first;
97 void CSVDialog::loadFile()
99 loadFileFromName(url);
102 void CSVDialog::guessSpecAndLoadFile()
105 spec.guessFromFile(url);
106 loadFileFromName(url);
111 void CSVDialog::loadFileFromName(
const std::string& fname)
115 initialLines.clear();
116 for (
size_t i=0; i<numInitialLines && is; ++i)
118 initialLines.emplace_back();
119 getline(is, initialLines.back());
121 if (!initialLines.back().empty() && initialLines.back().back()==
'\r')
122 initialLines.back().erase(initialLines.back().end()-1);
125 if (spec.dimensions.size()<spec.nColAxes()) spec.setDataArea(spec.nRowAxes(),spec.nColAxes());
128 template <
class Parser>
131 vector<vector<string>> r;
132 for (
const auto& line: lines)
137 const boost::tokenizer<Parser> tok(line.begin(), line.end(),
parser);
139 for (
size_t i=0; i<maxColumn && t!=tok.end(); ++i, ++t)
140 r.back().push_back(*t);
144 r.back().push_back(line);
156 CroppedPango(cairo_t* cairo,
double width): Pango(cairo), cairo(cairo), w(width) {}
157 void setxy(
double xx,
double yy) {x=xx; y=yy;}
159 const CairoSave cs(cairo);
160 cairo_rectangle(cairo,x,y,w,height());
162 cairo_move_to(cairo,x,y);
168 bool CSVDialog::redraw(
int,
int,
int,
int)
170 cairo_t* cairo=surface->cairo();
172 vector<vector<string>> parsedLines=
parseLines();
177 pango.setText(
"Dimension");
178 cairo_move_to(cairo,xoffs-pango.width()-5,0);
180 pango.setText(
"Type");
181 cairo_move_to(cairo,xoffs-pango.width()-5,rowHeight);
183 pango.setText(
"Format");
184 cairo_move_to(cairo,xoffs-pango.width()-5,2*rowHeight);
187 pango.setMarkup(
"<b>Name</b>");
189 pango.setText(
"Name");
190 cairo_move_to(cairo,xoffs-pango.width()-5,3*rowHeight);
192 pango.setText(
"Header");
193 cairo_move_to(cairo,xoffs-pango.width()-5,(4+spec.headerRow)*rowHeight);
198 CroppedPango pango(cairo, colWidth);
199 pango.setFontSize(0.8*rowHeight);
204 for (; done.size()<parsedLines.size(); ++col)
206 if (col<spec.nColAxes())
208 const CairoSave cs(cairo);
210 cairo_set_line_width(cairo,1);
211 cairo_translate(cairo,x+0.5*colWidth,y+0.5*rowHeight);
212 cairo_rectangle(cairo,-cbsz,-cbsz,2*cbsz,2*cbsz);
213 if (spec.dimensionCols.contains(col))
215 cairo_move_to(cairo,-cbsz,-cbsz);
216 cairo_line_to(cairo,cbsz,cbsz);
217 cairo_move_to(cairo,cbsz,-cbsz);
218 cairo_line_to(cairo,-cbsz,cbsz);
224 if (spec.dimensionCols.contains(col) && col<spec.dimensions.size() && col<spec.nColAxes())
226 pango.setText(classdesc::enumKey<Dimension::Type>(spec.dimensions[col].type));
231 if (spec.dimensionCols.contains(col) && col<spec.dimensions.size() && col<spec.nColAxes())
233 pango.setText(spec.dimensions[col].units);
238 if (spec.dimensionCols.contains(col) && col<spec.dimensionNames.size() && col<spec.nColAxes())
240 pango.setText(spec.dimensionNames[col]);
245 for (
size_t row=0; row<parsedLines.size(); ++row)
247 auto& line=parsedLines[row];
250 const CairoSave cs(cairo);
251 pango.setText(line[col]);
253 if (row==spec.headerRow)
254 if (col<spec.nColAxes())
255 cairo_set_source_rgb(surface->cairo(),0,0.7,0);
257 cairo_set_source_rgb(surface->cairo(),0,0,1);
258 else if (row<spec.nRowAxes() || (col<spec.nColAxes() && !spec.dimensionCols.contains(col)))
259 cairo_set_source_rgb(surface->cairo(),1,0,0);
260 else if (col<spec.nColAxes())
261 cairo_set_source_rgb(surface->cairo(),0,0,1);
269 const CairoSave cs(cairo);
270 cairo_set_source_rgb(cairo,.5,.5,.5);
271 cairo_move_to(cairo,x-2.5,0);
272 cairo_rel_line_to(cairo,0,(parsedLines.size()+4)*rowHeight);
278 m_tableWidth=(col-1)*(colWidth+5);
279 for (
size_t row=0; row<parsedLines.size()+5; ++row)
281 const CairoSave cs(cairo);
282 cairo_set_source_rgb(cairo,.5,.5,.5);
283 cairo_move_to(cairo,xoffs-2.5,row*rowHeight);
284 cairo_rel_line_to(cairo,m_tableWidth,0);
290 size_t CSVDialog::columnOver(
double x)
const 292 return size_t((x-xoffs)/(colWidth+5));
295 size_t CSVDialog::rowOver(
double y)
const 297 return size_t(y/rowHeight);
302 vector<vector<string>> parsedLines;
303 if (spec.mergeDelimiters)
304 if (spec.separator==
' ')
305 parsedLines=
::parseLines(boost::char_separator<char>(), initialLines, maxColumn);
308 char separators[]={spec.separator,
'\0'};
310 (boost::char_separator<char>(separators,
""),initialLines, maxColumn);
314 (boost::escaped_list_separator<char>(spec.escape,spec.separator,spec.quote),
315 initialLines, maxColumn);
318 if (maxColumn==numeric_limits<size_t>::max())
321 for (
auto& i: parsedLines)
322 spec.numCols=std::max(spec.numCols, i.size());
327 void CSVDialog::populateHeaders()
330 if (spec.headerRow>=parsedLines.size())
return;
331 auto& hr=parsedLines[spec.headerRow];
332 spec.dimensionNames={hr.begin(), min(hr.end(), hr.begin()+spec.maxColumn)};
335 void CSVDialog::populateHeader(
size_t col)
338 if (spec.headerRow>=parsedLines.size())
return;
339 auto& headers=parsedLines[spec.headerRow];
340 if (col<headers.size() && col<spec.maxColumn)
341 spec.dimensionNames[col]=headers[col];
344 void CSVDialog::classifyColumns()
347 spec.dimensionCols.clear();
348 spec.dataCols.clear();
349 spec.dimensions.resize(min(spec.numCols,spec.maxColumn));
350 for (
size_t col=0; col<spec.numCols; ++col)
352 bool entryFound=
false, timeFound=
true, numberFound=
true;
353 for (
size_t row=spec.nRowAxes(); row<parsedLines.size(); ++row)
354 if (col<parsedLines[row].size() && !parsedLines[row][col].empty())
357 if (numberFound && !
isNumerical(parsedLines[row][col]))
359 static const AnyVal any(Dimension(Dimension::time,
""));
362 {any(parsedLines[row][col]);}
366 if (entryFound && col<spec.maxColumn)
369 spec.dataCols.insert(col);
372 spec.dimensionCols.insert(col);
374 spec.dimensions[col].type=Dimension::time;
376 spec.dimensions[col].type=Dimension::string;
377 spec.dimensions[col].units.clear();
380 else if (col>=spec.nColAxes() && col<spec.maxColumn)
381 spec.dataCols.insert(col);
385 std::vector<size_t> CSVDialog::correctedUniqueValues()
387 auto r=spec.uniqueValues();
389 vector<set<size_t>> correction(r.size());
391 const hash<string> h;
392 for (
size_t row=0; row<parsedLines.size() && row<spec.nRowAxes(); ++row)
393 for (
size_t col=0; col<correction.size() && col<parsedLines[row].size(); ++col)
394 correction[col].insert(h(parsedLines[row][col]));
395 for (
size_t i=0; i<r.size(); ++i)
396 if (r[i]>correction[i].size())
397 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)
Creation and access to the minskyTCL_obj object, which has code to record whenever Minsky's state cha...
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 ...