First version of statistics (very ugly code)

This commit is contained in:
Grégory Soutadé 2010-08-17 18:59:19 +02:00
parent 023b1def9d
commit 74c401c8c7
15 changed files with 417 additions and 21 deletions

3
TODO
View File

@ -13,6 +13,8 @@ Better build system for wxFreeChart (hacked by me)
Others translation
Handle bad SQL return
Category color/font
Button tooltip not translated
Do not consider account transferts into statistics
===============================================================
Next version
@ -24,3 +26,4 @@ Undo/redo
Database auto saving at startup
Documentation
Use caches for created panels (avoid destroying/creating panels for nothing)
Printing

View File

@ -336,3 +336,15 @@ bool KissCount::SearchPreviousOperation(Operation* res, wxString& description, i
return true;
}
void KissCount::GetStats(int monthFrom, int yearFrom, int monthTo, int yearTo,
std::map<wxString, std::map<int, std::map<int, double> > >* accountAmounts,
std::map<wxString, double>* categories)
{
wxString monthF = wxString::Format(wxT("%d"), monthFrom);
wxString monthT = wxString::Format(wxT("%d"), monthTo);
wxString yearF = wxString::Format(wxT("%d"), yearFrom);
wxString yearT = wxString::Format(wxT("%d"), yearTo);
_db->GetStats(_user, monthF, yearF, monthT, yearT, accountAmounts, categories);
}

View File

@ -75,6 +75,11 @@ class KissCount
std::vector<wxString> categories, std::vector<wxString> accounts);
bool SearchPreviousOperation(Operation* res, wxString& description, int month, int year);
void GetStats(int monthFrom, int yearFrom, int monthTo, int yearTo,
std::map<wxString, std::map<int, std::map<int, double> > >* accountAmounts,
std::map<wxString, double>* categories);
private:
wxUI* _wxUI;
Database* _db;

View File

@ -365,7 +365,7 @@ void Database::UpdateOperation(Operation& op)
req += wxT(", checked='1'");
else
req += wxT(", checked='0'");
req += wxT(", forumla='") + op.formula + wxT("'");
req += wxT(", formula='") + op.formula + wxT("'");
req += wxT(" WHERE id='") + op.id + wxT("'");
EXECUTE_SQL_UPDATE(req, );
@ -1056,3 +1056,56 @@ std::vector<Operation>* Database::Search(User* user, wxString* description, wxDa
return res;
}
void Database::GetStats(User* user, const wxString& monthFrom, const wxString& yearFrom, const wxString& monthTo,
const wxString& yearTo, std::map<wxString, std::map<int, std::map<int, double> > >* accountAmounts,
std::map<wxString, double>* categories)
{
wxSQLite3ResultSet set;
wxString req;
std::vector<Account>::iterator accountIt;
std::vector<Category>::iterator categoryIt;
if (!user->_accounts.empty())
{
for (accountIt = user->_accounts.begin(); accountIt != user->_accounts.end(); accountIt++)
{
req = wxT("SELECT month, year, amount FROM account_amount WHERE account ='") + accountIt->id + wxT("'");
EXECUTE_SQL_QUERY(req, set, );
while (set.NextRow())
{
(*accountAmounts)[accountIt->id][set.GetInt(wxT("year"))][set.GetInt(wxT("month"))] = set.GetInt(wxT("amount"));
}
set.Finalize();
}
for (categoryIt = user->_categories.begin(); categoryIt != user->_categories.end(); categoryIt++)
{
req = wxT("SELECT SUM(amount) as amount FROM operation WHERE category='") + categoryIt->id + wxT("'");
accountIt = user->_accounts.begin();
req += wxT(" AND (account IN('") + accountIt->id;
accountIt++;
for (;accountIt != user->_accounts.end(); accountIt++)
{
req += wxT("', '") + accountIt->id ;
}
req += wxT("')");
req += wxT(" OR user='") + user->_id + wxT("')");
req += wxT(" AND (year > '") + yearFrom + wxT("' OR (year == '") + yearFrom + wxT("' AND month >= '") + monthFrom + wxT("'))");
req += wxT(" AND (year < '") + yearTo + wxT("' OR (year == '") + yearTo + wxT("' AND month <= '") + monthTo + wxT("'))");
req += wxT(" AND amount < 0");
EXECUTE_SQL_QUERY(req, set, );
if (set.NextRow())
{
(*categories)[categoryIt->id] = -set.GetDouble(wxT("amount"));
}
set.Finalize();
}
}
}

View File

@ -72,6 +72,10 @@ class Database
wxString* amountFrom, wxString* amountTo,
std::vector<wxString> categories, std::vector<wxString> accounts);
void GetStats(User* user, const wxString& monthFrom, const wxString& yearFrom, const wxString& monthTo,
const wxString& yearTo, std::map<wxString, std::map<int, std::map<int, double> > >* accountAmounts,
std::map<wxString, double>* categories);
void KillMe(User* user);
private:
wxSQLite3Database _db;

View File

@ -70,7 +70,7 @@ wxString User::GetCategoryId(wxString& catName)
return wxT("0") ;
}
wxString User::GetAccountName(wxString& accountId)
wxString User::GetAccountName(const wxString& accountId)
{
std::vector<Account>::iterator it;
for (it=_accounts.begin(); it !=_accounts.end(); it++)

View File

@ -45,7 +45,7 @@ class User
Category GetCategory(wxString& catId);
wxString GetCategoryName(wxString& catId);
wxString GetCategoryId(wxString& catName);
wxString GetAccountName(wxString& accountId);
wxString GetAccountName(const wxString& accountId);
wxString GetAccountId(wxString& accountName);
int GetCategoriesNumber();
int GetAccountsNumber();

View File

@ -46,18 +46,6 @@ AccountPanel::AccountPanel(KissCount* kiss, wxUI *parent) : wxScrolledWindow(&(*
User* user = _kiss->GetUser();
std::vector<Account>::iterator accountIt;
std::vector<Category>::iterator categoryIt;
wxColour categoryColors[] = {wxColour(0x00, 0x45, 0x86),
wxColour(0xFF, 0x3E, 0x0E),
wxColour(0xFF, 0xD3, 0x20),
wxColour(0x58, 0x9D, 0x1B),
wxColour(0x7E, 0x00, 0x21),
wxColour(0x83, 0xCC, 0xFF),
wxColour(0x31, 0x40, 0x04),
wxColour(0xB0, 0xCF, 0x00),
wxColour(0x4B, 0x1F, 0x6F),
wxColour(0xFF, 0x93, 0x0E),
wxColour(0xC5, 0x00, 0x0D),
wxColour(0x00, 0x84, 0xD1)};
DEFAULT_FONT(font);
SetSizer(hbox);

View File

@ -23,6 +23,7 @@ enum {BUTTON_ACCOUNT_ID=1, BUTTON_STATS_ID, BUTTON_SEARCH_ID, BUTTON_PREFS_ID, B
BEGIN_EVENT_TABLE(ButtonPanel, wxPanel)
EVT_BUTTON(BUTTON_ACCOUNT_ID, ButtonPanel::OnButtonAccount)
EVT_BUTTON(BUTTON_STATS_ID, ButtonPanel::OnButtonStats)
EVT_BUTTON(BUTTON_SEARCH_ID, ButtonPanel::OnButtonSearch)
EVT_BUTTON(BUTTON_PREFS_ID, ButtonPanel::OnButtonPreferences)
EVT_BUTTON(BUTTON_CHANGE_USER_ID, ButtonPanel::OnButtonChangeUser)
@ -76,6 +77,11 @@ void ButtonPanel::OnButtonAccount(wxCommandEvent& event)
_wxUI->ShowAccount();
}
void ButtonPanel::OnButtonStats(wxCommandEvent& event)
{
_wxUI->ShowStats();
}
void ButtonPanel::OnButtonSearch(wxCommandEvent& event)
{
_wxUI->ShowSearch();

View File

@ -39,6 +39,7 @@ class ButtonPanel: public wxPanel
~ButtonPanel();
void OnButtonAccount(wxCommandEvent& event);
void OnButtonStats(wxCommandEvent& event);
void OnButtonSearch(wxCommandEvent& event);
void OnButtonPreferences(wxCommandEvent& event);
void OnButtonChangeUser(wxCommandEvent& event);

View File

@ -571,7 +571,7 @@ void PreferencesPanel::OnLanguageChange(wxCommandEvent& event)
void PreferencesPanel::OnShow(wxShowEvent& event)
{
_wxUI->SetTitle(_kiss->GetUser()->_name + _(" - ") +_("Preferences"));
_wxUI->SetTitle(_kiss->GetUser()->_name + _(" - ") + _("Preferences"));
}
void PreferencesPanel::OnKillMe(wxCommandEvent& event)

214
src/view/StatsPanel.cpp Normal file
View File

@ -0,0 +1,214 @@
/*
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"
StatsPanel::StatsPanel(KissCount* kiss, wxUI *parent) : wxPanel(&(*parent)), _kiss(kiss), _wxUI(parent)
{
wxBoxSizer *hbox = new wxBoxSizer(wxHORIZONTAL);
wxBoxSizer *hbox2 = new wxBoxSizer(wxHORIZONTAL);
wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *vbox2 = new wxBoxSizer(wxVERTICAL);
int i;
User* user = _kiss->GetUser();
std::vector<Account>::iterator accountIt;
std::vector<Category>::iterator categoryIt;
SetSizer(vbox);
_monthFrom = new wxChoice (this, -1, wxDefaultPosition, wxDefaultSize, 12, months);
_monthTo = new wxChoice (this, -1, wxDefaultPosition, wxDefaultSize, 12, months);
hbox->Add(_monthFrom);
hbox->Add(_monthTo);
_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(_statsGrid);
_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));
UpdateStats(hbox2);
vbox2->Add(chart);
hbox2->Add(vbox2);
vbox->Add(hbox);
vbox->Add(hbox2);
Fit();
Hide();
}
void StatsPanel::UpdateStats(wxBoxSizer *hbox2)
{
std::map<wxString, std::map<int, std::map<int, double> > > accountAmounts;
std::map<wxString, double> categories;
std::map<wxString, double>::iterator categoriesIt;
std::map<wxString, std::map<int, std::map<int, double> > >::iterator accountIdIt;
std::map<int, std::map<int, double> >::iterator accountYearIt;
double total;
int size, i, a, b, percents;
double *amounts;
User* user = _kiss->GetUser();
wxString value;
_kiss->GetStats(0, 2010, 11, 2010, &accountAmounts, &categories);
// first step: create plot
_plot = new XYPlot();
// create dataset
XYSimpleDataset *dataset = new XYSimpleDataset();
// add two series
for (i = 0, accountIdIt = accountAmounts.begin(); accountIdIt != accountAmounts.end(); accountIdIt++, i++)
{
size = accountAmounts[accountIdIt->first].size();
amounts = new double[size*12*2];
size = 0;
for(a = 0, accountYearIt = accountAmounts[accountIdIt->first].begin();
accountYearIt != accountAmounts[accountIdIt->first].end();
accountYearIt++, a++)
{
for(b = 0; b<12; b++)
{
if (!accountAmounts[accountIdIt->first][accountYearIt->first].count(b))
continue;
amounts[size*2+0] = a*12+b;
amounts[size*2+1] = accountAmounts[accountIdIt->first][accountYearIt->first][b];
size++;
}
}
dataset->AddSerie((double *) amounts, size);
// set serie names to be displayed on legend
dataset->SetSerieName(i, user->GetAccountName(accountIdIt->first));
delete[] amounts;
}
// create line renderer and set it to dataset
XYLineRenderer *renderer = new XYLineRenderer();
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);
// create line marker
LineMarker *lineMarker = new LineMarker(wxColour(255, 0, 0), 2);
// set value to be marked, in our case horizontal value 15
lineMarker->SetHorizontalLine(25);
// and add marker to dataset
dataset->AddMarker(lineMarker);
// set legend
_plot->SetLegend(new Legend(wxCENTER, wxRIGHT));
wxChartPanel* chart = new wxChartPanel(this);
chart->SetChart(new Chart(_plot, _("Accounts")));
chart->Fit();
chart->Layout();
chart->SetMinSize(// chart->GetSize()
wxSize(750,550));
hbox2->Add(chart);
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;
percents = ((double) (categoriesIt->second*100))/total;
value = wxString::Format(wxT("%0.2lf (%d%%)"), categoriesIt->second, percents);
_statsGrid->SetCellValue(_categoriesIndexes[categoriesIt->first], 1, value);
}
_statsGrid->AutoSizeColumn(0, true);
_statsGrid->AutoSizeColumn(1, true);
_pie->DatasetChanged(_dataset);
}
void StatsPanel::OnShow(wxShowEvent& event)
{
_wxUI->SetTitle(_kiss->GetUser()->_name + _(" - ") + _("Statistics"));
}

71
src/view/StatsPanel.h Normal file
View File

@ -0,0 +1,71 @@
/*
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/>.
*/
#ifndef STATSPANEL_H
#define STATSPANEL_H
#include <wx/wx.h>
#include <wx/grid.h>
#include <wx/choice.h>
#include <wx/xy/xyplot.h>
#include <wx/xy/xylinerenderer.h>
#include <wx/xy/xysimpledataset.h>
#include <wx/chart.h>
#include <wx/grid.h>
#include <wx/pie/pieplot.h>
#include <wx/chartpanel.h>
#include <wx/category/categorysimpledataset.h>
#include <controller/KissCount.h>
#include "wxUI.h"
#include <model/model.h>
#include "view.h"
class wxUI;
class KissCount;
class StatsPanel: public wxPanel //public wxScrolledWindow
{
public:
StatsPanel(KissCount* kiss, wxUI *parent);
//~StatsPanel();
void OnShow(wxShowEvent& event);
private:
KissCount* _kiss;
wxUI* _wxUI;
wxCalendarCtrl* _calendarFrom, *_calendarTo;
wxChoice* _monthFrom, *_yearFrom, *_monthTo, *_yearTo;
wxGrid *_statsGrid;
PiePlot* _pie;
double *_categoriesValues;
CategorySimpleDataset* _dataset;
XYPlot *_plot ;
wxString* _categories;
std::map<wxString, int> _categoriesIndexes;
void UpdateStats(wxBoxSizer *);
//DECLARE_EVENT_TABLE();
};
#endif

View File

@ -20,10 +20,22 @@ along with KissCount. If not, see <http://www.gnu.org/licenses/>.
#include "wxUI.h"
wxString months[12] ;
wxColour categoryColors[12] = {wxColour(0x00, 0x45, 0x86),
wxColour(0xFF, 0x3E, 0x0E),
wxColour(0xFF, 0xD3, 0x20),
wxColour(0x58, 0x9D, 0x1B),
wxColour(0x7E, 0x00, 0x21),
wxColour(0x83, 0xCC, 0xFF),
wxColour(0x31, 0x40, 0x04),
wxColour(0xB0, 0xCF, 0x00),
wxColour(0x4B, 0x1F, 0x6F),
wxColour(0xFF, 0x93, 0x0E),
wxColour(0xC5, 0x00, 0x0D),
wxColour(0x00, 0x84, 0xD1)};
wxUI::wxUI(KissCount* kiss, const wxString& title, const wxPoint& pos, const wxSize& size)
: wxFrame(NULL, wxID_ANY, title, pos, size), _kiss(kiss), _accountPanel(NULL), _searchPanel(NULL), _preferencesPanel(NULL),
_curPanel(NULL), _locale(NULL), _needReload(false)
: wxFrame(NULL, wxID_ANY, title, pos, size), _kiss(kiss), _accountPanel(NULL), _statsPanel(NULL),
_searchPanel(NULL), _preferencesPanel(NULL), _curPanel(NULL), _locale(NULL), _needReload(false)
{
_hbox = new wxBoxSizer(wxVERTICAL);
ButtonPanel* buttons = new ButtonPanel(_kiss, this);
@ -51,6 +63,7 @@ wxUI::~wxUI()
if (_accountPanel) delete _accountPanel;
if (_preferencesPanel) delete _preferencesPanel;
if (_searchPanel) delete _searchPanel;
if (_statsPanel) delete _statsPanel;
if (_locale) delete _locale;
}
@ -117,6 +130,11 @@ void wxUI::ShowSearch()
ShowPanel(_searchPanel);
}
void wxUI::ShowStats()
{
ShowPanel(_statsPanel);
}
void wxUI::ShowPreferences()
{
ShowPanel(_preferencesPanel);
@ -147,10 +165,14 @@ void wxUI::LoadUser()
if (_searchPanel)
delete _searchPanel;
if (_statsPanel)
delete _statsPanel;
if (user->_preferences[wxT("language")] != wxT(""))
SetLanguage(user->GetLanguage());
_accountPanel = new AccountPanel(_kiss, this);
_statsPanel = new StatsPanel(_kiss, this);
_searchPanel = new SearchPanel(_kiss, this);
_preferencesPanel = new PreferencesPanel(_kiss, this);
@ -159,7 +181,7 @@ void wxUI::LoadUser()
void wxUI::ShowPanel(wxPanel* panel)
{
int month, year, account=0, preferences=0, search=0;
int month, year, account=0, preferences=0, search=0, stats=0;
wxShowEvent event;
if (!panel) return;
@ -189,9 +211,15 @@ void wxUI::ShowPanel(wxPanel* panel)
search = 1;
}
if (panel == _statsPanel)
{
stats = 1;
}
delete _accountPanel;
delete _preferencesPanel;
delete _searchPanel;
delete _statsPanel;
_accountPanel = new AccountPanel(_kiss, this);
if (year != -1)
@ -201,22 +229,28 @@ void wxUI::ShowPanel(wxPanel* panel)
}
_preferencesPanel = new PreferencesPanel(_kiss, this);
_searchPanel = new SearchPanel(_kiss, this);
_statsPanel = new StatsPanel(_kiss, this);
if (account)
{
_accountPanel->OnShow(event);
panel = _accountPanel;
}
if (preferences)
else if (preferences)
{
_preferencesPanel->OnShow(event);
panel = _preferencesPanel;
}
if (search)
else if (search)
{
_searchPanel->OnShow(event);
panel = _searchPanel;
}
else if (stats)
{
_statsPanel->OnShow(event);
panel = _statsPanel;
}
_needReload = false;
}

View File

@ -27,13 +27,16 @@ along with KissCount. If not, see <http://www.gnu.org/licenses/>.
#include "UsersDialog.h"
#include "GenerateDialog.h"
#include "SearchPanel.h"
#include "StatsPanel.h"
#include <controller/KissCount.h>
class KissCount;
class AccountPanel;
class PreferencesPanel;
class StatsPanel;
extern wxString months[12];
extern wxColour categoryColors[12];
class wxUI: public wxFrame
{
@ -48,6 +51,7 @@ class wxUI: public wxFrame
void LoadUser();
void ShowAccount();
void ShowStats();
void ShowSearch();
void ShowPreferences();
void GenerateMonth(int month, int year);
@ -63,6 +67,7 @@ class wxUI: public wxFrame
KissCount *_kiss;
wxBoxSizer *_hbox;
AccountPanel *_accountPanel;
StatsPanel *_statsPanel;
SearchPanel *_searchPanel;
PreferencesPanel *_preferencesPanel;
wxPanel *_curPanel;