KissCount/src/view/StatsPanel.cpp

369 lines
11 KiB
C++

/*
Copyright 2010 Grégory Soutadé
This file is part of KissCount.
KissCount is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
KissCount is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with KissCount. If not, see <http://www.gnu.org/licenses/>.
*/
#include "StatsPanel.h"
enum {RANGE_ID=1, ACCOUNTS_ID};
BEGIN_EVENT_TABLE(StatsPanel, wxPanel)
EVT_CHOICE(RANGE_ID, StatsPanel::OnRangeChange)
EVT_CHECKLISTBOX(ACCOUNTS_ID, StatsPanel::OnAccountsChange)
END_EVENT_TABLE()
StatsPanel::StatsPanel(KissCount* kiss, wxUI *parent) : KissPanel(kiss, parent), _plot(NULL), _chart(NULL)
{
wxBoxSizer *hbox = new wxBoxSizer(wxHORIZONTAL);
_hbox2 = new wxBoxSizer(wxHORIZONTAL);
wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
_vbox2 = new wxBoxSizer(wxVERTICAL);
int i;
User* user = _kiss->GetUser();
std::vector<Account>::iterator accountIt;
std::vector<Category>::iterator categoryIt;
std::map<int, std::vector<int> > operations;
std::map<int, std::vector<int> >::iterator it;
SetSizer(vbox);
_monthFrom = new wxChoice (this, RANGE_ID, wxDefaultPosition, wxDefaultSize, 12, months);
_yearFrom = new wxChoice (this, RANGE_ID);
_monthTo = new wxChoice (this, RANGE_ID, wxDefaultPosition, wxDefaultSize, 12, months);
_yearTo = new wxChoice (this, RANGE_ID);
operations = _kiss->GetAllOperations();
for(i=0, it = operations.begin(); it != operations.end(); it++, i++)
{
_yearFrom->Append(wxString::Format(wxT("%d"), it->first));
_yearTo->Append(wxString::Format(wxT("%d"), it->first));
}
if (i) i--;
_yearFrom->Select(i);
_yearTo->Select(i);
_monthFrom->Select(0);
_monthTo->Select(11);
wxStaticText* label = new wxStaticText(this, wxID_ANY, _("From"));
hbox->Add(label, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5);
hbox->Add(_monthFrom, 0, wxRIGHT, 5);
hbox->Add(_yearFrom, 0, wxRIGHT, 20);
label = new wxStaticText(this, wxID_ANY, _("To"));
hbox->Add(label, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5);
hbox->Add(_monthTo, 0, wxRIGHT, 5);
hbox->Add(_yearTo);
_account = new wxCheckListBox(this, ACCOUNTS_ID);
for(i=0, accountIt = user->_accounts.begin(); accountIt != user->_accounts.end(); accountIt++, i++)
{
_account->Append(accountIt->name);
_account->Check(i);
}
_categories = new wxString[user->GetCategoriesNumber()] ;
for(i=0, categoryIt = user->_categories.begin();
categoryIt != user->_categories.end();
categoryIt++, i++)
{
_categoriesIndexes[categoryIt->id] = i;
_categories[i] = categoryIt->name ;
}
DEFAULT_FONT(font);
_statsGrid = new wxGrid(this, wxID_ANY);
_statsGrid->CreateGrid(user->GetCategoriesNumber(), 2);
_statsGrid->SetColLabelSize(0);
_statsGrid->SetRowLabelSize(0);
_statsGrid->EnableEditing(false);
_statsGrid->SetDefaultCellFont(font);
_statsGrid->AutoSizeColumn(0, true);
for(i=0; i<user->GetCategoriesNumber(); i++)
{
_statsGrid->SetCellValue(i, 0, _categories[i]);
_statsGrid->SetCellAlignment(i, 1, wxALIGN_RIGHT, wxALIGN_CENTRE);
}
_vbox2->Add(_account, 0, wxGROW|wxALL, 5);
_vbox2->Add(_statsGrid, 0, wxALIGN_CENTER_HORIZONTAL|wxGROW|wxALL, 5);
_pie = new PiePlot();
_dataset = new CategorySimpleDataset(_categories, user->GetCategoriesNumber());
ColorScheme* colorScheme = new ColorScheme(categoryColors, WXSIZEOF(categoryColors));
_categoriesValues = new double[user->GetCategoriesNumber()];
for(i=0; i<user->GetCategoriesNumber(); i++)
_categoriesValues[i] = 0.0;
_dataset->AddSerie(_("Serie 1"), _categoriesValues, user->GetCategoriesNumber());
_dataset->SetRenderer(new CategoryRenderer(*colorScheme));
_pie->SetDataset(_dataset);
_pie->SetColorScheme(colorScheme);
_pie->SetLegend(new Legend(wxBOTTOM, wxCENTER));
wxChartPanel* chart = new wxChartPanel(this);
chart->SetChart(new Chart(_pie, _("Cost repartition")));
chart->Fit();
chart->Layout();
chart->SetMinSize(// chart->GetSize()
wxSize(200,250));
_vbox2->Add(chart, 0, wxALIGN_CENTER_HORIZONTAL|wxGROW|wxALL, 10);
vbox->Add(hbox, 0, wxALIGN_CENTER_VERTICAL|wxGROW|wxALL, 5);
vbox->Add(_hbox2, 0, wxGROW|wxALL, 5);
wxCommandEvent event ;
OnRangeChange(event);
Fit();
}
KissPanel* StatsPanel::CreatePanel()
{
return new StatsPanel(_kiss, _wxUI);
}
wxBitmapButton* StatsPanel::GetButton(int id)
{
if (!_KissButton)
_KissButton = new wxBitmapButton(_wxUI, id, wxBitmap(wxT(STATS_ICON)), wxDefaultPosition, wxSize(128, 128));
return _KissButton;
}
wxString StatsPanel::GetToolTip()
{
return _("Statistics");
}
void StatsPanel::UpdateStats(int monthFrom, int yearFrom, int monthTo, int yearTo)
{
std::map<wxString, std::map<int, std::map<int, double> > > accountAmounts;
std::map<wxString, double> categories;
std::map<wxString, std::vector<double> > operations;
std::vector<Account>::iterator accountIt;
std::map<wxString, double>::iterator categoriesIt;
std::map<int, std::map<int, double> >::iterator accountYearIt;
double total;
int account, size, i, a, b, percents, nbDays;
double *amounts;
wxString value;
User* user = _kiss->GetUser();
wxDateTime date;
if (_chart)
{
_hbox2->Detach(_chart);
_hbox2->Detach(_vbox2);
delete _chart;
}
// first step: create plot
_plot = new XYPlot();
// create dataset
XYSimpleDataset *dataset = new XYSimpleDataset();
if (monthFrom == monthTo && yearFrom == yearTo)
{
nbDays = date.GetLastMonthDay((wxDateTime::Month)monthFrom, yearFrom).GetDay();
_kiss->GetMonthStats(monthFrom, yearFrom, nbDays, &operations, &categories);
// Line on 0 all over the years
amounts = new double[nbDays*2];
for (a=0; a<nbDays; a++)
{
amounts[a*2+0] = a;
amounts[a*2+1] = 0;
}
dataset->AddSerie((double *) amounts, nbDays);
delete[] amounts;
for (account = 0, i = 0, accountIt = user->_accounts.begin(); accountIt != user->_accounts.end();
account++, accountIt++, i++)
{
if (!_account->IsChecked(account))
{
i-- ;
continue;
}
amounts = new double[nbDays*2];
size = 0;
for (a=0; a<nbDays; a++)
{
amounts[a*2+0] = a;
amounts[a*2+1] = operations[accountIt->id][a];
}
dataset->AddSerie((double *) amounts, nbDays);
// set serie names to be displayed on legend
dataset->SetSerieName(i+1, user->GetAccountName(accountIt->id));
delete[] amounts;
}
}
else
{
_kiss->GetStats(monthFrom, yearFrom, monthTo, yearTo, &accountAmounts, &categories);
// Line on 0 all over the years
size = ((yearTo - yearFrom) + 1) * 12;
amounts = new double[size*2];
for (a=0; a<(size/12); a++)
{
for(b=0; b<12; b++)
{
amounts[a*12*2+b*2+0] = a*12+b;
amounts[a*12*2+b*2+1] = 0;
}
}
dataset->AddSerie((double *) amounts, size);
delete[] amounts;
for (account = 0, i = 0, accountIt = user->_accounts.begin(); accountIt != user->_accounts.end();
account++, accountIt++, i++)
{
if (!_account->IsChecked(account))
{
i-- ;
continue;
}
size = accountAmounts[accountIt->id].size();
amounts = new double[size*12*2];
size = 0;
for(a = 0, accountYearIt = accountAmounts[accountIt->id].begin();
accountYearIt != accountAmounts[accountIt->id].end();
accountYearIt++, a++)
{
for(b = 0; b<12; b++)
{
if (!accountAmounts[accountIt->id][accountYearIt->first].count(b))
continue;
amounts[size*2+0] = a*12+b;
amounts[size*2+1] = accountAmounts[accountIt->id][accountYearIt->first][b];
size++;
}
}
dataset->AddSerie((double *) amounts, size);
// set serie names to be displayed on legend
dataset->SetSerieName(i+1, user->GetAccountName(accountIt->id));
delete[] amounts;
}
}
// create line renderer and set it to dataset
XYLineRenderer *renderer = new XYLineRenderer(true, true);
dataset->SetRenderer(renderer);
// add our dataset to plot
_plot->AddDataset(dataset);
// create left and bottom number axes
NumberAxis *leftAxis = new NumberAxis(AXIS_LEFT);
NumberAxis *bottomAxis = new NumberAxis(AXIS_BOTTOM);
// add axes to plot
_plot->AddAxis(leftAxis);
_plot->AddAxis(bottomAxis);
// link axes and dataset
_plot->LinkDataVerticalAxis(0, 0);
_plot->LinkDataHorizontalAxis(0, 0);
// set legend
_plot->SetLegend(new Legend(wxCENTER, wxRIGHT));
_chart = new wxChartPanel(this);
_chart->SetChart(new Chart(_plot, _("Accounts")));
_chart->Fit();
_chart->Layout();
_chart->SetMinSize(// chart->GetSize()
wxSize(750,550));
_hbox2->Add(_chart, 0, wxGROW|wxALL, 5);
total = 0.0;
for(categoriesIt = categories.begin(); categoriesIt != categories.end(); categoriesIt++)
total += categoriesIt->second;
for(categoriesIt = categories.begin(); categoriesIt != categories.end(); categoriesIt++)
{
_categoriesValues[_categoriesIndexes[categoriesIt->first]] = categoriesIt->second;
if (total)
percents = ((double) (categoriesIt->second*100))/total;
else
percents = 0;
value = wxString::Format(wxT("%0.2lf (%02d%%)"), categoriesIt->second, percents);
_statsGrid->SetCellValue(_categoriesIndexes[categoriesIt->first], 1, value);
}
_statsGrid->AutoSizeColumn(0, true);
_statsGrid->AutoSizeColumn(1, true);
_pie->DatasetChanged(_dataset);
_hbox2->Add(_vbox2, 0, wxGROW|wxALL, 5);
Layout();
}
void StatsPanel::OnShow(wxShowEvent& event)
{
_wxUI->SetTitle(_kiss->GetUser()->_name + _(" - ") + _("Statistics"));
}
void StatsPanel::OnRangeChange(wxCommandEvent& event)
{
long monthFrom, monthTo, yearFrom, yearTo;
if (!_yearFrom->GetStringSelection().Length() ||
!_yearTo->GetStringSelection().Length())
return;
monthFrom = _monthFrom->GetCurrentSelection();
_yearFrom->GetStringSelection().ToLong(&yearFrom);
monthTo = _monthTo->GetCurrentSelection();
_yearTo->GetStringSelection().ToLong(&yearTo);
if (yearTo < yearFrom ||
(yearFrom == yearTo && monthFrom > monthTo))
{
wxMessageBox(_("Invalide date range"), _("KissCount"), wxICON_ERROR | wxOK);
return;
}
UpdateStats(monthFrom, yearFrom, monthTo, yearTo);
}
void StatsPanel::OnAccountsChange(wxCommandEvent& event)
{
OnRangeChange(event);
}