YATiSh
Yet Another TIme SHeet
yatishPlot.cpp
Go to the documentation of this file.
1 /********************************************************************
2  * Name: yatishPlot.cpp
3  * Purpose: Implements the (virtual) class for yatish data plots
4  * Author: Nicolas PĂ©renne (nicolas.perenne@eif-services.eu)
5  * Created: 2023-07-05
6  * Copyright: EIF-services (https://www.eif-services.eu/yatish)
7  * License: GPLv3
8  ********************************************************************/
9 
10 #include "wx_pch.h"
11 #include "yatishPlot.h"
12 
13 #define CHART_HEIGHT 250
14 
15 //static members
17 string yatishPlot::legends [3];
19 bool yatishPlot::data_loaded = false;
21 
22 // colorbrewer2.org
23 wxColour colors[] = {
24  wxColour (127, 201, 127),
25  wxColour (190, 174, 212),
26  wxColour (253, 192, 134),
27  wxColour (255, 255, 153)
28 };
29 
37  if (data_loaded) return;
38  if ( !pdb->FillPlotData (big_data) )
39  wxLogError ("Data loading failed (panel #3)");
40  Aggregates();
41  formerAggregate = invalid; // to force first PlotData()
42  data_loaded = true;
43  // vector_X and vector_X2 (prepared for plotting libraries) won't change in a given yatish run
44  wxDateTime weekCenter;
45  time_t epochTime;
46  wxDateTime::wxDateTime_t weekNumber = 0, weekOffset = 0;
47  for (ProcessedData::iterator it = data.begin(); it != data.end(); it++) {
48  weekCenter = it->weekCenter;
49  weekCenter.MakeFromUTC(); // weekCenter: now localtime
50  epochTime = weekCenter.GetTicks();
51  vector_X.push_back (epochTime); // auto-conversion: time_t->double
52  if (weekCenter.GetWeekOfYear() < weekNumber) // the year has changed
53  weekOffset += weekNumber;
54  weekNumber = weekCenter.GetWeekOfYear();
55  vector_X2.push_back (weekNumber + weekOffset); // auto-conversion: wxDateTime_t->double
56  }
57 }
58 
64 void yatishPlot::GetWeekRange (unsigned int & firstWeek, unsigned int & lastWeek) {
65  wxDateTime weekCenter;
66  wxDateTime::wxDateTime_t weekNumber = 0, weekOffset = 0,
67  firstOffset = 0, lastOffset = 0; // to get rid of a warning
68  int yearCenter, firstYear = firstDay.GetYear(), lastYear = lastDay.GetYear();
69  for (ProcessedData::size_type j = 0; j < data.size(); j++) {
70  weekCenter = data[j].weekCenter;
71  if (weekCenter.GetWeekOfYear() < weekNumber) weekOffset += weekNumber;
72  weekNumber = weekCenter.GetWeekOfYear();
73  yearCenter = weekCenter.GetYear();
74  if (yearCenter == firstYear) firstOffset = weekOffset;
75  if (yearCenter == lastYear ) lastOffset = weekOffset;
76  if (yearCenter >= firstYear && yearCenter >= lastYear) break;
77  }
78  if (firstDay.GetMonth() == wxDateTime::Jan && firstDay.GetDay() == 1)
79  firstWeek = 1.; // first of January is likely to belong to the last week of the previous year
80  else
81  firstWeek = firstDay.GetWeekOfYear();
82  firstWeek += firstOffset;
83  lastWeek = lastDay.GetWeekOfYear() + lastOffset;
84 }
85 
91  int offset = -1;
92  wxDateTime::wxDateTime_t currentWeek = 0;
93  wxDateTime::WeekDay currentDay = wxDateTime::Inv_WeekDay;
94 
95  wxDateTime start, stop, firstStartDaily, emptyWeek;
96  wxTimeSpan span, timeDaily, timeRangeDaily, noTime = wxTimeSpan::Hours (0);
97  int recordCount = 0;
98  ProcessedRecord a, b;
99  for (RawData::iterator it = big_data.begin(); it != big_data.end(); it++) {
100  start = it->start; stop = it->stop; span = stop - start;
101  if (start.GetWeekOfYear() == currentWeek) {
102  data[offset].timeTotal += span;
103  data[offset].minutesAtimeslot = data[offset].timeTotal.GetSeconds().ToDouble()/++recordCount/60.;
104  if (start.GetWeekDay() == currentDay) {
105  timeDaily += span;
106  // assuming Records are not overlapping (i.e. Record.stop keeps increasing)
107  timeRangeDaily = stop - firstStartDaily;
108  } else {
109  data[offset].daysWorked++;
110  timeDaily = span;
111  firstStartDaily = start; timeRangeDaily = stop - firstStartDaily;
112  currentDay = start.GetWeekDay();
113  }
114  if (data[offset].timeMax < timeDaily ) data[offset].timeMax = timeDaily;
115  if (data[offset].timeRangeMax < timeRangeDaily) data[offset].timeRangeMax = timeRangeDaily;
116  } else { // including first iteration
117  timeDaily = span;
118  firstStartDaily = start; timeRangeDaily = stop - firstStartDaily;
119  currentDay = start.GetWeekDay();
120  start.SetToWeekDayInSameWeek (wxDateTime::Thu); start.ResetTime(); start.SetHour (12);
121  a.weekCenter = start;
122  a.timeTotal = span;
123  a.daysWorked = 1;
124  a.timeMax = span;
125  a.timeRangeMax = stop - firstStartDaily;
126  a.minutesAtimeslot = span.GetSeconds().ToDouble()/60.;
127  if (currentWeek > 0) { // excluding first iteration
128  emptyWeek = data[offset].weekCenter + wxDateSpan::Week();
129  while ( emptyWeek.IsEarlierThan (a.weekCenter) ) { // fill in empty values if need be
130  b.weekCenter = emptyWeek;
131  b.timeTotal = noTime;
132  b.daysWorked = 0;
133  b.timeMax = noTime;
134  b.timeRangeMax = noTime;
135  b.minutesAtimeslot = 0.;
136  data.push_back (b); offset++;
137  emptyWeek += wxDateSpan::Week();
138  }
139  }
140  data.push_back (a); offset++;
141  currentWeek = start.GetWeekOfYear();
142  recordCount = 1;
143  }
144  }
145 }
146 
153  pieData.clear();
154  // Compute total time per cluster; each timeslot is rounded down to the minute:
155  wxDateTime start;
156  wxTimeSpan span;
157  unsigned long minutes;
158  string key;
159  wxASSERT (clusters != none);
160  for (RawData::iterator it = big_data.begin(); it != big_data.end(); it++) {
161  start = it->start;
162  if (start < firstDay || start > lastDay) continue;
163  span = it->stop - start;
164  minutes = span.GetHours()*60 + span.GetMinutes();
165  switch (clusters) {
166  case clientClusters:
167  key = it->client .ToStdString();
168  break;
169  case projectClusters:
170  key = it->project.ToStdString();
171  break;
172  case taskClusters:
173  key = it->task .ToStdString();
174  break;
175  default: //toolClusters
176  key = it->tool .ToStdString();
177  }
178  pieData[key] += minutes;
179  }
180  NoMoreThan4();
181  // Store the largest cluster names (drop "others"):
182  Map4pie::iterator it; int i;
183  for (i = 0; i < 3; i++)
184  legends[i].clear();
185  string others = _("others").ToStdString();
186  for (it = pieData.begin(), i = 0; it != pieData.end(); it++)
187  if (it->first != others)
188  legends[i++] = it->first;
189 }
190 
195  unsigned long mini;
196  string key, others = _("others").ToStdString();
197  if (pieData.size() == 4) { // we need to label the smallest as "others" because there will be only 3 curves anyway
198  mini = ULONG_MAX;
199  for (auto element : pieData)
200  if (element.second < mini) {
201  mini = element.second;
202  key = element.first;
203  }
204  pieData[others] = mini;
205  pieData.erase (pieData.find (key) );
206  }
207  while (pieData.size() > 4) {
208  mini = ULONG_MAX;
209  for (auto element : pieData) {
210  if (element.first == others) continue;
211  if (element.second < mini) {
212  mini = element.second;
213  key = element.first;
214  }
215  }
216  pieData[others] += mini;
217  pieData.erase (pieData.find (key) );
218  }
219 }
220 
226  unsigned long totalMinutes = 0;
227  for (auto element : pieData)
228  totalMinutes += element.second;
229  double totalHours = totalMinutes/60.;
230  double totalDays = totalHours/7.;
231  return wxString::Format(_("SUM: %.1f hours (%.1f days)"), totalHours, totalDays);
232 }
233 
239  int offset = -1;
240  wxDateTime::wxDateTime_t currentWeek = 0;
241  wxDateTime start;
242  wxTimeSpan span, noTime = wxTimeSpan::Hours (0);
243  double span_hours;
244  string s;
245  int i, j;
246 
247  wxASSERT (clusters != none);
248  for (RawData::iterator it = big_data.begin(); it != big_data.end(); it++) {
249  start = it->start;
250  span = it->stop - start;
251  span_hours = span.GetSeconds().ToDouble()/3600.;
252  switch (clusters) {
253  case clientClusters:
254  s = it->client .ToStdString();
255  break;
256  case projectClusters:
257  s = it->project.ToStdString();
258  break;
259  case taskClusters:
260  s = it->task .ToStdString();
261  break;
262  default: //toolClusters
263  s = it->tool .ToStdString();
264  }
265  i = 0;
266  while (s != legends[i] && i < 3) i++;
267  if (start.GetWeekOfYear() == currentWeek) {
268  if (i < 3) data[offset].hours[i] += span_hours;
269  } else { // including first iteration
270  currentWeek = start.GetWeekOfYear();
271  do offset++;
272  while (data[offset].timeTotal == noTime); // skip eventual empty weeks
273  for (j = 0; j < 3; j++) data[offset].hours[j] = 0.;
274  if (i < 3) data[offset].hours[i] = span_hours;
275  }
276  }
277 }
278 
284  if (clusters != none) { // modify the choice of clustered series [through Pies()]
285  ComputePie (clusters);
286  ClusteredSeries (clusters);
287  }
288  if (clusters == none && aggregate == formerAggregate) { // nothing to do apart from setting X range
289  SetRangeX();
290  return true;
291  }
292 
293  for (int i = 0; i < 3; i++)
294  vector_Y[i].clear();
295  ClearCurves();
296 
297  ProcessedData::iterator it;
298  wxASSERT (aggregate != invalid);
299  if (clusters == none) {
300  for (it = data.begin(); it != data.end(); it++) {
301  switch (aggregate) {
302  case hoursTotal:
303  vector_Y[0].push_back (it->timeTotal.GetSeconds().ToDouble()/3600.);
304  break;
305  case daysWorked:
306  vector_Y[0].push_back ((double)it->daysWorked);
307  break;
308  case hoursAdayMax:
309  vector_Y[0].push_back (it->timeMax.GetSeconds().ToDouble()/3600.);
310  break;
311  case timeRangeMax:
312  vector_Y[0].push_back (it->timeRangeMax.GetSeconds().ToDouble()/3600.);
313  break;
314  default: //minutesAtimeslot
315  vector_Y[0].push_back (it->minutesAtimeslot);
316  }
317  }
318  formerAggregate = aggregate;
319  } else {
320  for (it = data.begin(); it != data.end(); it++)
321  for (int i = 0; i < 3; i++)
322  vector_Y[i].push_back (it->hours[i]);
324  }
325 
326  return false;
327 }
328 
329 /***************************
330  Graphic library: wxMathPlot
331  ***************************/
332 
334  : yatishPlot (pdb), mpWindow (p, wxID_ANY) {
335  SetMinSize ( wxSize (-1, CHART_HEIGHT) );
336  Xaxis = new mpScaleX (_("weeks"), mpALIGN_BORDER_BOTTOM, true, mpX_DATE);
337  AddLayer (Xaxis, false);
338  Yaxis = new mpScaleY (_("hours"), mpALIGN_BORDER_LEFT);
339  AddLayer (Yaxis, false);
340  //SetMargins (0, 0, 0, 0);
341  EnableDoubleBuffer (true);
342  wxPen pen [3] = { *wxBLUE_PEN, *wxGREEN_PEN, *wxRED_PEN };
343  wxString title;
344  for (int i = 0; i < 3; i++) {
345  title.Printf ("vector1%d", i);
346  vector1[i] = new mpFXYVector (title); // initial title otherwise mpInfoLegend gets confused
347  vector1[i]->SetContinuity (true);
348  title.Printf ("vector2%d", i);
349  vector2[i] = new mpFXYVector (title);
350  pen[i].SetWidth (5); vector2[i]->SetPen (pen[i]);
351  AddLayer (vector1[i], false);
352  AddLayer (vector2[i], false);
353  }
354  int w, h;
355  p->GetSize (&w, &h);
356  legend = new mpInfoLegend ( wxRect (w/2, 0, 50, 50) );
357  legend->SetItemMode (mpLEGEND_SQUARE);
358  AddLayer (legend);
359  legend->SetVisible (true);
360 }
361 
363  if ( SelectData (clusters, aggregate) ) return;
364  if (clusters == none) {
365  wxASSERT ( vector_X.size() == vector_Y[0].size() );
366  vector1[0]->SetData (vector_X, vector_Y[0]);
367  vector2[0]->SetData (vector_X, vector_Y[0]);
368  vector1[0]->SetVisible (true);
369  vector2[0]->SetVisible (true);
370  for (int i = 1; i < 3; i++) {
371  vector1[i]->SetVisible (false);
372  vector2[i]->SetVisible (false);
373  }
374  legend->SetVisible (false);
375  if (aggregate == daysWorked)
376  Yaxis->SetName(_("days") );
377  else if (aggregate == minutesAtimeslot)
378  Yaxis->SetName(_("minutes") );
379  else
380  Yaxis->SetName(_("hours") );
381  } else {
382  for (int i = 0; i < 3 && legends[i].size(); i++) {
383  vector1[i]->SetData (vector_X, vector_Y[i]);
384  vector2[i]->SetData (vector_X, vector_Y[i]);
385  vector1[i]->SetName ( wxString (legends[i]) ); // wxEmptyString confuses mpInfoLegend
386  vector2[i]->SetName ( wxString (legends[i]) );
387  vector1[i]->SetVisible (true);
388  vector2[i]->SetVisible (true);
389  }
390  legend->SetVisible (true);
391  Yaxis->SetName(_("hours") );
392  }
393  SetRangeX();
394 }
395 
397  UpdateBBox(); // mpWindow::m_minY and mpWindow::m_maxY are set there
398  Fit (firstDay.GetTicks(), lastDay.GetTicks() + 86400, m_minY, m_maxY); // auto-conversion: time_t->double
399 }
400 
402  for (int i = 0; i < 3; i++) {
403  vector1[i]->Clear(); vector1[i]->SetVisible (false);
404  vector2[i]->Clear(); vector2[i]->SetVisible (false);
405  }
406 }
407 
408 /****************************
409  Graphic library: wxFreeChart
410  ****************************/
411 
413  : yatishPlot (pdb), wxChartPanel (p) {
414  SetMinSize ( wxSize (-1, CHART_HEIGHT) );
415 #ifdef wxUSE_GRAPHICS_CONTEXT
416  SetAntialias(true);
417 #endif
418  dataset = new XYSimpleDataset();
419  wxVector<wxRealPoint> vector_XY [3]; // we need a few temporary points to start with
420  for (int i = 0; i < 3; i++) {
421  vector_XY[i].push_back (wxRealPoint (1, i ) );
422  vector_XY[i].push_back (wxRealPoint (2, i+1) );
423  series[i] = new XYSerie (vector_XY[i]);
424  series[i]->SetName ( wxString::Format ("#%d", i) ); // temporary name
425  dataset->AddSerie (series[i]);
426  }
427  renderer = new XYLineRenderer (true, true);
428  renderer->SetSeriePen ( 0, wxThePenList->FindOrCreatePen (*wxBLUE, 2, wxPENSTYLE_SOLID) );
429  renderer->SetSeriePen ( 1, wxThePenList->FindOrCreatePen (*wxGREEN, 2, wxPENSTYLE_SOLID) );
430  renderer->SetSeriePen ( 2, wxThePenList->FindOrCreatePen (*wxRED, 2, wxPENSTYLE_SOLID) );
431  dataset->SetRenderer (renderer);
432  plot = new XYPlot();
433  plot->AddDataset (dataset);
434  leftAxis = new NumberAxis (AXIS_LEFT);
435  bottomAxis = new NumberAxis (AXIS_BOTTOM);
436  leftAxis ->SetTickFormat ("%.0f");
437  bottomAxis->SetTickFormat ("%.0f");
438  bottomAxis->ZeroOrigin (false);
439  bottomAxis->SetTitle (_("weeks") );
440  plot->AddAxis (leftAxis);
441  plot->AddAxis (bottomAxis);
442  plot->LinkDataVerticalAxis (0, 0);
443  plot->LinkDataHorizontalAxis (0, 0);
444  legend = new Legend (wxCENTER, wxRIGHT);
445  plot->SetLegend (legend);
446  chart = new Chart(plot, NULL, NULL);
447  SetChart (chart);
448 }
449 
451  if ( SelectData (clusters, aggregate) ) return;
452  if (clusters == none) {
453  wxASSERT ( vector_X2.size() == vector_Y[0].size() );
454  for (ProcessedData::size_type j = 0; j < vector_X.size(); j++)
455  series[0]->Append ( wxRealPoint (vector_X2[j], vector_Y[0][j]) );
456  if (aggregate == daysWorked)
457  leftAxis->SetTitle(_("days") );
458  else if (aggregate == minutesAtimeslot)
459  leftAxis->SetTitle(_("minutes") );
460  else
461  leftAxis->SetTitle(_("hours") );
462  } else {
463  for (int i = 0; i < 3 && legends[i].size(); i++) {
464  for (ProcessedData::size_type j = 0; j < vector_X2.size(); j++)
465  series[i]->Append ( wxRealPoint (vector_X2[j], vector_Y[i][j]) );
466  series[i]->SetName ( wxString (legends[i]) );
467  }
468  leftAxis->SetTitle(_("hours") );
469  }
470  SetRangeX();
471  dataset->DatasetChanged();
472 }
473 
475  unsigned int firstWeek, lastWeek;
476  GetWeekRange (firstWeek, lastWeek);
477  bottomAxis->SetFixedBounds (firstWeek, lastWeek); // unsigned int -> double
478  dataset->DatasetChanged();
479 }
480 
482  int i; size_t j;
483  for (i = 0; i < 3; i++) {
484  j = series[i]->GetCount();
485  while (j > 0) series[i]->Remove (--j);
486  series[i]->SetName (" ");
487  }
488 }
489 
491  : yatishPlot (pdb), wxChartPanel (p) {
492  SetMinSize ( wxSize (-1, CHART_HEIGHT) );
493 #ifdef wxUSE_GRAPHICS_CONTEXT
494  SetAntialias(true);
495 #endif
496  dataset = new CategorySimpleDataset (categories, 4);
497  dataset->AddSerie (wxEmptyString, data, 4);
498  colorScheme = new ColorScheme ( colors, WXSIZEOF(colors) );
499  renderer = new CategoryRenderer (*colorScheme);
500  dataset->SetRenderer (renderer);
501  plot = new PiePlot();
502  plot->SetColorScheme (colorScheme);
503  plot->SetDataset (dataset);
504  legend = new Legend (wxCENTER, wxRIGHT);
505  plot->SetLegend (legend);
506  chart = new Chart(plot, NULL, NULL);
507  SetChart (chart);
508 }
509 
511  wxASSERT (clusters != none);
512  ComputePie (clusters);
513  Map4pie::iterator it; int i;
514  for (i = 0; i < 4; i++) {
515  categories[i] = wxEmptyString;
516  data[i] = 0.;
517  }
518  for (it = pieData.begin(), i = 0; it != pieData.end(); it++) {
519  categories[i] = wxString (it->first);
520  data[i++] = it->second;
521  }
522  dataset = new CategorySimpleDataset (categories, 4);
523  dataset->AddSerie (wxEmptyString, data, 4);
524  dataset->SetRenderer (renderer);
525  plot->SetDataset (dataset); // ~CategorySimpleDataset [and thus ~Dataset()] are called here
526  dataset->DatasetChanged();
527 }
528 
529 /*************************
530  Graphic library: wxCharts
531  *************************/
532 
534  : yatishPlot (pdb), wxPanel (p) {
535  SetMinSize ( wxSize (-1, CHART_HEIGHT) );
536  panelSizer = new wxBoxSizer (wxHORIZONTAL);
537  SetSizer (panelSizer);
538  lineChartON = legendON = false;
539 }
540 
542  currentClusters = clusters;
543  ResetChart(); // with this library we must recreate everything anyway
544  if ( SelectData (clusters, aggregate) ) return;
545  for (ProcessedData::size_type j = 0; j < vector_X.size(); j++) {
546  labels.push_back ( wxString::Format ("%.0f", vector_X2[j]) );
547  if (clusters == none)
548  points[0].push_back (vector_Y[0][j]);
549  else
550  for (int i = 0; i < 3 && legends[i].size(); i++)
551  points[i].push_back (vector_Y[i][j]);
552  }
553  CreateChart();
554 }
555 
557  if (legendON) {
558  panelSizer->Detach (legendCtrl);
559  legendCtrl->Destroy();
560  legendON = false;
561  }
562  if (lineChartON) {
563  panelSizer->Detach (lineChartCtrl);
564  lineChartCtrl->Destroy();
565  lineChartON = false;
566  }
567  for (int i = 0; i < 3; i++) {
568  dataset[i].reset(); // => delete data[i] (?) [when dataset[i].use_count() = 0]
569  points[i].clear();
570  }
571  labels.clear();
572  chartData.reset();
573 }
574 
576  chartData = wxChartsCategoricalData::make_shared (labels);
577  if (currentClusters == none) {
578  data[0] = new wxChartsDoubleDataset (wxEmptyString, points[0]);
579  dataset[0].reset (data[0]);
580  chartData->AddDataset (dataset[0]);
581  } else
582  for (int i = 0; i < 3 && legends[i].size(); i++) {
583  data[i] = new wxChartsDoubleDataset (wxString (legends[i]), points[i]);
584  dataset[i].reset (data[i]);
585  chartData->AddDataset (dataset[i]);
586  }
587  lineChartCtrl = new wxLineChartCtrl (this, wxID_ANY, chartData, wxCHARTSLINETYPE_STRAIGHT,
588  wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
589  panelSizer->Add (lineChartCtrl, 5, wxEXPAND);
590  lineChartON = true;
591  if (currentClusters != none) {
592  wxChartsLegendData legendData ( chartData->GetDatasets() );
593  legendCtrl = new wxChartsLegendCtrl (this, wxID_ANY, legendData,
594  wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
595  panelSizer->Add (legendCtrl, 1, wxEXPAND);
596  legendON = true;
597  }
598  panelSizer->Fit (this);
599 }
600 
602  ResetChart(); // with this library we must recreate everything anyway
603  unsigned int firstWeek, lastWeek;
604  GetWeekRange (firstWeek, lastWeek);
605  for (ProcessedData::size_type j = 0; j < vector_X.size(); j++) {
606  if (vector_X2[j] < firstWeek) continue;
607  if (vector_X2[j] > lastWeek) break;
608  labels.push_back ( wxString::Format ("%.0f", vector_X2[j]) );
609  if (currentClusters == none)
610  points[0].push_back (vector_Y[0][j]);
611  else
612  for (int i = 0; i < 3 && legends[i].size(); i++)
613  points[i].push_back (vector_Y[i][j]);
614  }
615  CreateChart();
616 }
617 
619  : yatishPlot (pdb), wxPanel (p) {
620  SetMinSize ( wxSize (-1, CHART_HEIGHT) );
621  panelSizer = new wxBoxSizer (wxHORIZONTAL);
622  SetSizer (panelSizer);
623  pieChartON = legendON = false;
624 }
625 
627  ResetChart(); // UpdateSlices() won't change labels :-(
628  chartData = wxPieChartData::make_shared();
629  ComputePie (clusters);
630  Map4pie::iterator it; int i;
631  for (it = pieData.begin(), i = 0; it != pieData.end(); it++)
632  slices.push_back ( wxChartSliceData ( it->second, colors[i++], wxString (it->first) ) );
633  chartData->AddSlices (slices);
634  pieChartCtrl = new wxPieChartCtrl (this, wxID_ANY, chartData,
635  wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
636  pieChartON = true;
637  wxChartsLegendData legendData ( chartData->GetSlices() );
638  legendCtrl = new wxChartsLegendCtrl(this, wxID_ANY, legendData,
639  wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
640  legendON = true;
641  panelSizer->Add (pieChartCtrl, 5, wxEXPAND);
642  panelSizer->Add (legendCtrl, 1, wxEXPAND);
643  panelSizer->Fit (this);
644 }
645 
647  if (legendON) {
648  panelSizer->Detach (legendCtrl);
649  legendCtrl->Destroy();
650  legendON = false;
651  }
652  if (pieChartON) {
653  panelSizer->Detach (pieChartCtrl);
654  pieChartCtrl->Destroy();
655  pieChartON = false;
656  }
657  slices.clear();
658  chartData.reset();
659 }
void PlotData(ClusterType, AggregateType)
Plotting interface for yatishFrame:
Definition: yatishPlot.cpp:626
yatishChartPie(wxWindow *, yatishDBsqlite *)
Definition: yatishPlot.cpp:618
wxPieChartData::ptr chartData
Definition: yatishPlot.h:167
wxVector< wxChartSliceData > slices
Definition: yatishPlot.h:168
wxPieChartCtrl * pieChartCtrl
Definition: yatishPlot.h:169
wxChartsLegendCtrl * legendCtrl
Definition: yatishPlot.h:170
wxBoxSizer * panelSizer
Definition: yatishPlot.h:166
wxChartsDoubleDataset * data[3]
Definition: yatishPlot.h:147
wxChartsLegendCtrl * legendCtrl
Definition: yatishPlot.h:150
wxChartsCategoricalData::ptr chartData
Definition: yatishPlot.h:146
wxBoxSizer * panelSizer
Definition: yatishPlot.h:143
yatishChartSerie(wxWindow *, yatishDBsqlite *)
Definition: yatishPlot.cpp:533
wxLineChartCtrl * lineChartCtrl
Definition: yatishPlot.h:149
wxVector< wxString > labels
Definition: yatishPlot.h:144
wxChartsDoubleDataset::ptr dataset[3]
Definition: yatishPlot.h:148
void PlotData(ClusterType, AggregateType)
Plotting interface for yatishFrame:
Definition: yatishPlot.cpp:541
wxVector< wxDouble > points[3]
Definition: yatishPlot.h:145
ClusterType currentClusters
Definition: yatishPlot.h:152
Interacts with yatish tables in a SQLite database.
bool FillPlotData(RawData &)
Fills a RawData (typedefined in yatishTypes.h).
CategoryRenderer * renderer
Definition: yatishPlot.h:126
ColorScheme * colorScheme
Definition: yatishPlot.h:125
double data[4]
Definition: yatishPlot.h:123
wxString categories[4]
Definition: yatishPlot.h:122
CategorySimpleDataset * dataset
Definition: yatishPlot.h:124
Legend * legend
Definition: yatishPlot.h:128
void PlotData(ClusterType, AggregateType)
Plotting interface for yatishFrame:
Definition: yatishPlot.cpp:510
yatishFreePie(wxWindow *, yatishDBsqlite *)
Definition: yatishPlot.cpp:490
Chart * chart
Definition: yatishPlot.h:129
PiePlot * plot
Definition: yatishPlot.h:127
void PlotData(ClusterType, AggregateType)
Plotting interface for yatishFrame:
Definition: yatishPlot.cpp:450
NumberAxis * bottomAxis
Definition: yatishPlot.h:106
XYSimpleDataset * dataset
Definition: yatishPlot.h:102
XYPlot * plot
Definition: yatishPlot.h:105
yatishFreeSerie(wxWindow *, yatishDBsqlite *)
Definition: yatishPlot.cpp:412
XYSerie * series[3]
Definition: yatishPlot.h:103
Legend * legend
Definition: yatishPlot.h:107
XYLineRenderer * renderer
Definition: yatishPlot.h:104
NumberAxis * leftAxis
Definition: yatishPlot.h:106
mpFXYVector * vector2[3]
Definition: yatishPlot.h:85
mpInfoLegend * legend
Definition: yatishPlot.h:88
yatishMathPlot(wxWindow *, yatishDBsqlite *)
Definition: yatishPlot.cpp:333
mpScaleY * Yaxis
Definition: yatishPlot.h:87
void ClearCurves()
Definition: yatishPlot.cpp:401
mpScaleX * Xaxis
Definition: yatishPlot.h:86
void PlotData(ClusterType, AggregateType)
Plotting interface for yatishFrame:
Definition: yatishPlot.cpp:362
mpFXYVector * vector1[3]
Definition: yatishPlot.h:85
Abstract Base class for the plots of panel #3.
Definition: yatishPlot.h:34
virtual void ClearCurves()
Definition: yatishPlot.h:68
wxString TotalFormattedFromPie()
Creates a wxString with the total hours in the current pie chart.
Definition: yatishPlot.cpp:225
yatishPlot(yatishDBsqlite *)
The constructor of yatishPlot:
Definition: yatishPlot.cpp:36
static vector< double > vector_Y[3]
user selected data
Definition: yatishPlot.h:59
static RawData big_data
all timeslot records from the database
Definition: yatishPlot.h:71
virtual void SetRangeX()
Definition: yatishPlot.h:67
void Aggregates()
Builds all the weekly time series (non-clustered aggregates).
Definition: yatishPlot.cpp:90
wxDateTime lastDay
Definition: yatishPlot.h:61
void NoMoreThan4()
Modifies member pieData so that there is no more than four record.
Definition: yatishPlot.cpp:194
wxDateTime firstDay
Definition: yatishPlot.h:61
static vector< double > vector_X2
week number, counted from the first year in the time series
Definition: yatishPlot.h:58
static vector< double > vector_X
epoch time
Definition: yatishPlot.h:57
Map4pie pieData
total durations (between firstDay and lastDay), in minutes
Definition: yatishPlot.h:60
bool SelectData(ClusterType, AggregateType)
Updates vector_Y[3] if need be (for the plotting libraries).
Definition: yatishPlot.cpp:283
AggregateType formerAggregate
Definition: yatishPlot.h:62
static bool data_loaded
Definition: yatishPlot.h:70
void ComputePie(ClusterType)
Fills member pieData with data for a pie chart with 4 sectors.
Definition: yatishPlot.cpp:152
void ClusteredSeries(ClusterType)
Updates the clustered time series.
Definition: yatishPlot.cpp:238
void GetWeekRange(unsigned int &, unsigned int &)
Computes the week numbers corresponding to members firstDay and lastDay.
Definition: yatishPlot.cpp:64
static ProcessedData data
weekly times series: various aggregates and clustered sums
Definition: yatishPlot.h:55
static string legends[3]
names of the 3 weekly clustered sums, for the current clustering scheme
Definition: yatishPlot.h:56
Element of typefined std:vector ProcessedData (yatishPlot.data).
Definition: yatishTypes.h:28
wxTimeSpan timeRangeMax
Definition: yatishTypes.h:31
wxDateTime weekCenter
Definition: yatishTypes.h:29
wxTimeSpan timeTotal
Definition: yatishTypes.h:31
double minutesAtimeslot
Definition: yatishTypes.h:33
wxTimeSpan timeMax
Definition: yatishTypes.h:31
#define CHART_HEIGHT
Definition: yatishPlot.cpp:13
wxColour colors[]
Definition: yatishPlot.cpp:23
ClusterType
Same order as yatishFrame::choiceCluster.
Definition: yatishTypes.h:58
@ taskClusters
Definition: yatishTypes.h:61
@ none
Definition: yatishTypes.h:63
@ projectClusters
Definition: yatishTypes.h:60
@ clientClusters
Definition: yatishTypes.h:59
vector< RawRecord > RawData
Definition: yatishTypes.h:25
vector< ProcessedRecord > ProcessedData
Definition: yatishTypes.h:38
AggregateType
Same order as yatishFrame::choiceAggregate (except invalid of course).
Definition: yatishTypes.h:47
@ timeRangeMax
Definition: yatishTypes.h:52
@ daysWorked
Definition: yatishTypes.h:50
@ minutesAtimeslot
Definition: yatishTypes.h:53
@ invalid
Definition: yatishTypes.h:48
@ hoursTotal
Definition: yatishTypes.h:49
@ hoursAdayMax
Definition: yatishTypes.h:51