[hamster-applet] humble beginnings of statistics
- From: Toms Baugis <tbaugis src gnome org>
- To: svn-commits-list gnome org
- Subject: [hamster-applet] humble beginnings of statistics
- Date: Sat, 13 Jun 2009 18:39:26 -0400 (EDT)
commit 452d255657740889c853d8ee31a4c6eab65c3721
Author: Toms Bauģis <toms baugis gmail com>
Date: Sat Jun 13 23:37:48 2009 +0100
humble beginnings of statistics
data/stats.ui | 842 ++++++++++++++++++++++++++++++++++++------------------
hamster/stats.py | 273 +++++++++++++++++-
2 files changed, 822 insertions(+), 293 deletions(-)
---
diff --git a/data/stats.ui b/data/stats.ui
index 3b34071..d56559e 100644
--- a/data/stats.ui
+++ b/data/stats.ui
@@ -15,281 +15,331 @@
<signal name="key_press_event" handler="on_window_key_pressed"/>
<signal name="delete_event" handler="on_close"/>
<child>
- <object class="GtkVBox" id="vbox1">
+ <object class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="top_padding">12</property>
+ <property name="bottom_padding">12</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
<child>
- <object class="GtkToolbar" id="toolbar3">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="toolbar_style">both-horiz</property>
- <child>
- <object class="GtkToolButton" id="prev">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="tooltip_text" translatable="yes">Earlier activities</property>
- <property name="stock_id">gtk-go-back</property>
- <signal name="clicked" handler="on_prev_clicked"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkToolButton" id="home">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="tooltip_text" translatable="yes">Today</property>
- <property name="label" translatable="yes">Today</property>
- <property name="use_underline">True</property>
- <property name="stock_id">gtk-home</property>
- <signal name="clicked" handler="on_home_clicked"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkToolButton" id="next">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="tooltip_text" translatable="yes">Newer activities</property>
- <property name="stock_id">gtk-go-forward</property>
- <signal name="clicked" handler="on_next_clicked"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkSeparatorToolItem" id="toolbutton3">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- </object>
- <packing>
- <property name="expand">False</property>
- </packing>
- </child>
- <child>
- <object class="GtkToolButton" id="remove">
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="tooltip_text" translatable="yes">Delete activity</property>
- <property name="stock_id">gtk-remove</property>
- <signal name="clicked" handler="on_remove_clicked"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkToolButton" id="add">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="tooltip_text" translatable="yes">Add activity</property>
- <property name="stock_id">gtk-add</property>
- <signal name="clicked" handler="on_add_clicked"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkToolButton" id="edit">
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="tooltip_text" translatable="yes">Edit activity</property>
- <property name="stock_id">gtk-edit</property>
- <signal name="clicked" handler="on_edit_clicked"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkSeparatorToolItem" id="toolbutton1">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- </object>
- <packing>
- <property name="expand">False</property>
- </packing>
- </child>
- <child>
- <object class="GtkRadioToolButton" id="week">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="tooltip_text" translatable="yes">Show week</property>
- <property name="is_important">True</property>
- <property name="label" translatable="yes"> _Week</property>
- <property name="use_underline">True</property>
- <property name="icon_widget">week_icon</property>
- <property name="active">True</property>
- <signal name="toggled" handler="on_week_toggled"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkRadioToolButton" id="month">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="tooltip_text" translatable="yes">Show month</property>
- <property name="is_important">True</property>
- <property name="label" translatable="yes"> _Month</property>
- <property name="use_underline">True</property>
- <property name="icon_widget">month_icon</property>
- <signal name="toggled" handler="on_month_toggled"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkSeparatorToolItem" id="<separator>">
- <property name="visible">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkToolButton" id="report_button">
- <property name="visible">True</property>
- <property name="tooltip_text" translatable="yes">Save as HTML</property>
- <property name="label" translatable="yes">Save as HTML</property>
- <property name="use_underline">True</property>
- <property name="stock_id">gtk-save</property>
- <signal name="clicked" handler="on_report_button_clicked"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkAlignment" id="alignment1">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="top_padding">12</property>
- <child>
- <object class="GtkLabel" id="overview_label">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="xalign">0</property>
- <property name="xpad">12</property>
- <property name="label" translatable="yes"><b>Overview</b></property>
- <property name="use_markup">True</property>
- <property name="single_line_mode">True</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkHPaned" id="hpaned1">
+ <object class="GtkNotebook" id="notebook1">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="border_width">12</property>
- <property name="position">300</property>
+ <property name="tab_hborder">8</property>
+ <property name="tab_vborder">4</property>
<child>
- <object class="GtkScrolledWindow" id="scrolledwindow1">
- <property name="width_request">300</property>
+ <object class="GtkVBox" id="vbox1">
<property name="visible">True</property>
- <property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="hscrollbar_policy">automatic</property>
- <property name="vscrollbar_policy">automatic</property>
- <property name="shadow_type">in</property>
+ <property name="orientation">vertical</property>
<child>
- <object class="GtkTreeView" id="facts">
+ <object class="GtkToolbar" id="toolbar3">
<property name="visible">True</property>
- <property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="enable_search">False</property>
- <signal name="key_press_event" handler="on_key_pressed"/>
- <signal name="row_activated" handler="on_facts_row_activated"/>
+ <property name="toolbar_style">both-horiz</property>
+ <child>
+ <object class="GtkToolButton" id="prev">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="stock_id">gtk-go-back</property>
+ <signal name="clicked" handler="on_prev_clicked"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="home">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Today</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-home</property>
+ <signal name="clicked" handler="on_home_clicked"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="next">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="stock_id">gtk-go-forward</property>
+ <signal name="clicked" handler="on_next_clicked"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparatorToolItem" id="toolbutton3">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="remove">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="stock_id">gtk-remove</property>
+ <signal name="clicked" handler="on_remove_clicked"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="add">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="stock_id">gtk-add</property>
+ <signal name="clicked" handler="on_add_clicked"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="edit">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="stock_id">gtk-edit</property>
+ <signal name="clicked" handler="on_edit_clicked"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparatorToolItem" id="toolbutton1">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioToolButton" id="week">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="is_important">True</property>
+ <property name="label" translatable="yes"> _Week</property>
+ <property name="use_underline">True</property>
+ <property name="icon_widget">week_icon</property>
+ <property name="active">True</property>
+ <signal name="toggled" handler="on_week_toggled"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioToolButton" id="month">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="is_important">True</property>
+ <property name="label" translatable="yes"> _Month</property>
+ <property name="use_underline">True</property>
+ <property name="icon_widget">month_icon</property>
+ <property name="active">True</property>
+ <signal name="toggled" handler="on_month_toggled"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparatorToolItem" id="<separator>">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="report_button">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Save as HTML</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-save</property>
+ <signal name="clicked" handler="on_report_button_clicked"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
</object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="alignment2">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="top_padding">12</property>
+ <child>
+ <object class="GtkLabel" id="overview_label">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="xalign">0</property>
+ <property name="xpad">12</property>
+ <property name="label" translatable="yes"><b>Overview</b></property>
+ <property name="use_markup">True</property>
+ <property name="single_line_mode">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
</child>
- </object>
- <packing>
- <property name="resize">False</property>
- <property name="shrink">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkScrolledWindow" id="scrolledwindow2">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="hscrollbar_policy">automatic</property>
- <property name="vscrollbar_policy">automatic</property>
- <property name="shadow_type">in</property>
<child>
- <object class="GtkEventBox" id="graph_frame">
+ <object class="GtkHPaned" id="hpaned1">
<property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="border_width">12</property>
+ <property name="position">300</property>
+ <property name="position_set">True</property>
<child>
- <object class="GtkVBox" id="vbox4">
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="width_request">300</property>
<property name="visible">True</property>
- <property name="orientation">vertical</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
<child>
- <object class="GtkVBox" id="graphs">
- <property name="width_request">390</property>
+ <object class="GtkTreeView" id="facts">
<property name="visible">True</property>
+ <property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="border_width">4</property>
- <property name="orientation">vertical</property>
- <property name="spacing">24</property>
- <property name="homogeneous">True</property>
+ <property name="enable_search">False</property>
+ <signal name="key_press_event" handler="on_key_pressed"/>
+ <signal name="row_activated" handler="on_facts_row_activated"/>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">False</property>
+ <property name="shrink">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkEventBox" id="graph_frame">
+ <property name="visible">True</property>
<child>
- <object class="GtkFrame" id="fram1">
+ <object class="GtkVBox" id="vbox4">
<property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label_xalign">0</property>
- <property name="shadow_type">none</property>
+ <property name="orientation">vertical</property>
<child>
- <object class="GtkHBox" id="hbox1">
+ <object class="GtkVBox" id="graphs">
+ <property name="width_request">390</property>
<property name="visible">True</property>
- <property name="border_width">9</property>
- <property name="spacing">36</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="border_width">4</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">24</property>
+ <property name="homogeneous">True</property>
<child>
- <object class="GtkEventBox" id="totals_by_category">
- <property name="width_request">50</property>
+ <object class="GtkFrame" id="fram1">
<property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
<child>
- <placeholder/>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="border_width">9</property>
+ <property name="spacing">36</property>
+ <child>
+ <object class="GtkEventBox" id="totals_by_category">
+ <property name="width_request">50</property>
+ <property name="visible">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEventBox" id="totals_by_day">
+ <property name="visible">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="dayview_caption">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Week</property>
+ <property name="use_markup">True</property>
+ </object>
</child>
</object>
<packing>
- <property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
- <object class="GtkEventBox" id="totals_by_day">
+ <object class="GtkFrame" id="frame39">
<property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
<child>
- <placeholder/>
+ <object class="GtkEventBox" id="totals_by_activity">
+ <property name="visible">True</property>
+ <property name="border_width">12</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Activity</property>
+ <property name="use_markup">True</property>
+ </object>
</child>
</object>
<packing>
@@ -297,87 +347,301 @@
</packing>
</child>
</object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
</child>
- <child type="label">
- <object class="GtkLabel" id="dayview_caption">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">Week</property>
- <property name="use_markup">True</property>
+ <child>
+ <object class="GtkLabel" id="no_data_label">
+ <property name="label" translatable="yes">No data for this interval</property>
</object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
</child>
</object>
- <packing>
- <property name="position">0</property>
- </packing>
</child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">True</property>
+ <property name="shrink">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">300</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Overview</property>
+ </object>
+ <packing>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="border_width">8</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkAlignment" id="alignment4">
+ <property name="visible">True</property>
+ <property name="yscale">0</property>
+ <child>
+ <object class="GtkTable" id="table1">
+ <property name="visible">True</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">4</property>
+ <property name="row_spacing">8</property>
+ <child>
+ <object class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Year:</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="alignment5">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="xscale">0</property>
<child>
- <object class="GtkFrame" id="frame39">
+ <object class="GtkHBox" id="year_box">
<property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label_xalign">0</property>
- <property name="shadow_type">none</property>
<child>
- <object class="GtkEventBox" id="totals_by_activity">
+ <object class="GtkToggleButton" id="togglebutton2">
+ <property name="label" translatable="yes">All</property>
+ <property name="width_request">60</property>
<property name="visible">True</property>
- <property name="border_width">12</property>
- <child>
- <placeholder/>
- </child>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
</object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
</child>
- <child type="label">
- <object class="GtkLabel" id="label3">
+ <child>
+ <object class="GtkToggleButton" id="togglebutton1">
+ <property name="label" translatable="yes">2009</property>
<property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">Activity</property>
- <property name="use_markup">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
</object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
</child>
</object>
- <packing>
- <property name="position">1</property>
- </packing>
</child>
</object>
<packing>
- <property name="position">0</property>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
</packing>
</child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow3">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">never</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkEventBox" id="explore_frame">
+ <property name="visible">True</property>
<child>
- <object class="GtkLabel" id="no_data_label">
- <property name="label" translatable="yes">No data for this interval</property>
+ <object class="GtkAlignment" id="alignment3">
+ <property name="visible">True</property>
+ <property name="top_padding">12</property>
+ <property name="bottom_padding">12</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox66">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkAlignment" id="explore_everything">
+ <property name="height_request">50</property>
+ <property name="visible">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTable" id="table2">
+ <property name="visible">True</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">3</property>
+ <child>
+ <object class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes"><b>Starts and ends</b></property>
+ <property name="use_markup">True</property>
+ </object>
+ <packing>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes"><b>Totals</b></property>
+ <property name="use_markup">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes"><b>Averages</b></property>
+ <property name="use_markup">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="explore_weekday_starts_ends">
+ <property name="visible">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="explore_category_starts_ends">
+ <property name="visible">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="explore_weekday_totals">
+ <property name="visible">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="explore_category_totals">
+ <property name="visible">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
</object>
- <packing>
- <property name="position">1</property>
- </packing>
</child>
</object>
</child>
</object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
</child>
</object>
<packing>
- <property name="resize">True</property>
- <property name="shrink">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Statistics</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ <property name="tab_fill">False</property>
</packing>
</child>
</object>
- <packing>
- <property name="position">2</property>
- </packing>
</child>
</object>
</child>
</object>
- <object class="GtkImage" id="week_icon">
- <property name="visible">True</property>
- <property name="pixbuf">art/stock_calendar-view-week.png</property>
- </object>
- <object class="GtkImage" id="month_icon">
- <property name="visible">True</property>
- <property name="pixbuf">art/stock_calendar-view-month.png</property>
- </object>
<object class="GtkFileChooserDialog" id="save_report_dialog">
<property name="border_width">5</property>
<property name="title" translatable="yes">Save report - Time Tracker</property>
@@ -553,4 +817,12 @@
<action-widget response="0">cancel_button</action-widget>
</action-widgets>
</object>
+ <object class="GtkImage" id="week_icon">
+ <property name="visible">True</property>
+ <property name="pixbuf">art/stock_calendar-view-week.png</property>
+ </object>
+ <object class="GtkImage" id="month_icon">
+ <property name="visible">True</property>
+ <property name="pixbuf">art/stock_calendar-view-month.png</property>
+ </object>
</interface>
diff --git a/hamster/stats.py b/hamster/stats.py
index 9d30320..448a729 100644
--- a/hamster/stats.py
+++ b/hamster/stats.py
@@ -32,6 +32,8 @@ from hamster.edit_activity import CustomFactController
from hamster import reports, widgets
import webbrowser
+from itertools import groupby
+
import datetime as dt
import calendar
import time
@@ -160,20 +162,20 @@ class StatsViewer(object):
self.parent = parent# determine if app shut shut down on close
self._gui = stuff.load_ui_file("stats.ui")
self.window = self.get_widget('stats_window')
+ self.stat_facts = None
#id, caption, duration, date (invisible), description, category
self.fact_store = gtk.TreeStore(int, str, str, str, str, str)
self.setup_tree()
- graph_frame = self.get_widget("graph_frame")
- background = (0.975,0.975,0.975)
- graph_frame.modify_bg(gtk.STATE_NORMAL,
- gtk.gdk.Color(*[int(b*65536.0) for b in background]))
+ self.background = (0.975,0.975,0.975)
+ self.get_widget("graph_frame").modify_bg(gtk.STATE_NORMAL,
+ gtk.gdk.Color(*[int(b*65536.0) for b in self.background]))
x_offset = 90 # let's nicely align all graphs
- self.category_chart = charting.BarChart(background = background,
+ self.category_chart = charting.BarChart(background = self.background,
bar_base_color = (238,221,221),
bars_beveled = False,
legend_width = x_offset,
@@ -185,7 +187,7 @@ class StatsViewer(object):
category_box.set_size_request(130, -1)
- self.day_chart = charting.BarChart(background = background,
+ self.day_chart = charting.BarChart(background = self.background,
bar_base_color = (220, 220, 220),
bars_beveled = False,
show_scale = True,
@@ -201,7 +203,7 @@ class StatsViewer(object):
stretch_grid = True,
legend_width = x_offset,
value_format = "%.1f",
- background = background,
+ background = self.background,
bars_beveled = False,
animate = False)
self.get_widget("totals_by_activity").add(self.activity_chart);
@@ -240,6 +242,261 @@ class StatsViewer(object):
self.report_chooser = None
self.do_graph()
+ self.init_stats()
+
+
+
+ def init_stats(self):
+ self.get_widget("explore_frame").modify_bg(gtk.STATE_NORMAL,
+ gtk.gdk.Color(*[int(b*65536.0) for b in self.background]))
+
+ if not self.stat_facts:
+ self.stat_facts = storage.get_facts(dt.date(1970, 1, 1), dt.date.today())
+
+ by_year = self._totals(self.stat_facts,
+ lambda fact: fact["start_time"].year,
+ lambda fact: 1)
+
+ year_box = self.get_widget("year_box")
+ for child in year_box.get_children():
+ year_box.remove(child)
+
+ class YearButton(gtk.ToggleButton):
+ def __init__(self, label, year, on_clicked):
+ gtk.ToggleButton.__init__(self, label)
+ self.year = year
+ self.connect("clicked", on_clicked)
+
+ all_button = YearButton(_("All"), None, self.on_year_changed)
+ year_box.pack_start(all_button)
+ self.bubbling = True # TODO figure out how to properly work with togglebuttons as radiobuttons
+ all_button.set_active(True)
+ self.bubbling = False # TODO figure out how to properly work with togglebuttons as radiobuttons
+
+ years = sorted(by_year.keys())
+ for year in years:
+ year_box.pack_start(YearButton(str(year), year, self.on_year_changed))
+
+ year_box.show_all()
+
+
+
+
+ self.chart_everything = charting.BarChart(value_format = "%.1f",
+ bars_beveled = False,
+ animate = False,
+ background = self.background,
+ max_bar_width = 35,
+ show_labels = False)
+ self.get_widget("explore_everything").add(self.chart_everything)
+
+
+ self.chart_category_totals = charting.HorizontalBarChart(value_format = "%.1f",
+ bars_beveled = False,
+ animate = False,
+ background = self.background,
+ max_bar_width = 20,
+ legend_width = 70)
+ self.get_widget("explore_category_totals").add(self.chart_category_totals)
+
+
+ self.chart_weekday_totals = charting.HorizontalBarChart(value_format = "%.1f",
+ bars_beveled = False,
+ animate = False,
+ background = self.background,
+ max_bar_width = 20,
+ legend_width = 70)
+ self.get_widget("explore_weekday_totals").add(self.chart_weekday_totals)
+
+ self.chart_weekday_starts_ends = charting.HorizontalDayChart(bars_beveled = False,
+ animate = False,
+ background = self.background,
+ max_bar_width = 20,
+ legend_width = 70)
+ self.get_widget("explore_weekday_starts_ends").add(self.chart_weekday_starts_ends)
+
+ self.chart_category_starts_ends = charting.HorizontalDayChart(bars_beveled = False,
+ animate = False,
+ background = self.background,
+ max_bar_width = 20,
+ legend_width = 70)
+ self.get_widget("explore_category_starts_ends").add(self.chart_category_starts_ends)
+
+ self.stats(self.stat_facts)
+
+ def on_year_changed(self, button):
+ if self.bubbling:
+ return
+
+ for child in button.parent.get_children():
+ if child != button and child.get_active():
+ self.bubbling = True
+ child.set_active(False)
+ self.bubbling = False
+
+ facts = self.stat_facts
+ if button.year:
+ facts = filter(lambda fact: fact["start_time"].year == button.year,
+ facts)
+
+ self.stats(facts)
+
+ def stats(self, facts):
+ # All dates in the scope
+ just_totals = self._totals(facts,
+ lambda fact: fact["start_time"].date(),
+ lambda fact: fact["delta"].seconds / 60 / 60.0)
+ just_totals_keys = sorted(just_totals.keys())
+
+ just_totals = [just_totals[key][0] for key in just_totals_keys]
+ just_totals_keys = [key.strftime("%d %m %Y") for key in just_totals_keys]
+
+ self.chart_everything.plot(just_totals_keys, just_totals)
+
+
+ # Totals by category
+ categories = self._totals(facts,
+ lambda fact: fact["category"],
+ lambda fact: fact['delta'].seconds / 60 / 60.0)
+ category_keys = sorted(categories.keys())
+ categories = [categories[key][0] for key in category_keys]
+ self.chart_category_totals.plot(category_keys, categories)
+
+
+ # Totals by weekday
+ weekdays = self._totals(facts,
+ lambda fact: (fact["start_time"].weekday(),
+ fact["start_time"].strftime("%a")),
+ lambda fact: fact['delta'].seconds / 60 / 60.0)
+
+ weekday_keys = sorted(weekdays.keys(), key = lambda x: x[0]) #sort
+ weekdays = [weekdays[key][0] for key in weekday_keys] #get values in the order
+ weekday_keys = [key[1] for key in weekday_keys] #now remove the weekday and keep just the abbreviated one
+
+ self.chart_weekday_totals.plot(weekday_keys, weekdays)
+
+
+
+ # now we will try to figure out average start and end times.
+ # first we need to group facts by date
+ # they are already sorted in db, so we can rely on that
+ by_weekday = {}
+ for date, date_facts in groupby(facts, lambda fact: fact["start_time"].date()):
+ date_facts = list(date_facts)
+ weekday = (date_facts[0]["start_time"].weekday(),
+ date_facts[0]["start_time"].strftime("%a"))
+ by_weekday.setdefault(weekday, {"min": [], "max": []})
+
+ min_time = min([fact["start_time"].time() for fact in date_facts])
+ by_weekday[weekday]["min"].append(min_time.minute + min_time.hour * 60)
+
+ same_date_facts = [fact["end_time"].time() for fact in date_facts if fact["end_time"] and fact["start_time"].date() == fact["end_time"].date()]
+ if same_date_facts:
+ max_time = max(same_date_facts)
+ by_weekday[weekday]["max"].append(max_time.minute + max_time.hour * 60)
+
+ for day in by_weekday:
+ by_weekday[day]["min"] = int(sum(by_weekday[day]["min"]) / float(len(by_weekday[day]["min"])))
+ by_weekday[day]["min"] = dt.time(by_weekday[day]["min"] / 60, by_weekday[day]["min"] % 60)
+
+ by_weekday[day]["max"] = int(sum(by_weekday[day]["max"]) / float(len(by_weekday[day]["max"])))
+ by_weekday[day]["max"] = dt.time(by_weekday[day]["max"] / 60, by_weekday[day]["max"] % 60)
+
+ min_weekday = min([by_weekday[day]["min"] for day in by_weekday])
+ max_weekday = max([by_weekday[day]["max"] for day in by_weekday])
+
+ weekday_keys = sorted(by_weekday.keys(), key = lambda x: x[0])
+ weekdays = [(by_weekday[key]["min"], by_weekday[key]["max"])
+ for key in weekday_keys]
+
+ weekday_keys = [key[1] for key in weekday_keys] # get rid of the weekday number as int
+
+
+ # and now try to figure out average min and max times per day per category
+
+ # now we will try to figure out average start and end times.
+ # first we need to group facts by date
+ # they are already sorted in db, so we can rely on that
+ by_category = {}
+ for date, date_facts in groupby(facts, lambda fact: fact["start_time"].date()):
+ date_facts = sorted(list(date_facts), key = lambda x: x["category"])
+
+ for category, category_facts in groupby(date_facts, lambda x: x["category"]):
+ category_facts = list(category_facts)
+ by_category.setdefault(category, {"min": [], "max": []})
+
+ min_time = min([fact["start_time"].time() for fact in category_facts])
+ by_category[category]["min"].append(min_time.minute + min_time.hour * 60)
+
+ same_date_facts = [fact["end_time"].time() for fact in category_facts if fact["end_time"] and fact["start_time"].date() == fact["end_time"].date()]
+ if same_date_facts:
+ max_time = max(same_date_facts)
+ by_category[category]["max"].append(max_time.minute + max_time.hour * 60)
+
+ for key in by_category:
+ by_category[key]["min"] = int(sum(by_category[key]["min"]) / float(len(by_category[key]["min"])))
+ by_category[key]["min"] = dt.time(by_category[key]["min"] / 60, by_category[key]["min"] % 60)
+
+ by_category[key]["max"] = int(sum(by_category[key]["max"]) / float(len(by_category[key]["max"])))
+ by_category[key]["max"] = dt.time(by_category[key]["max"] / 60, by_category[key]["max"] % 60)
+
+ min_category = min([by_category[day]["min"] for day in by_category])
+ max_category = max([by_category[day]["max"] for day in by_category])
+
+
+ category_keys = sorted(by_category.keys(), key = lambda x: x[0])
+ categories = [(by_category[key]["min"], by_category[key]["max"])
+ for key in category_keys]
+
+
+ #get starting and ending hours for graph and turn them into exact hours that divide by 3
+ min_hour = min([min_weekday, min_category])
+ min_hour = dt.time((min_hour.hour * 60 + min_hour.minute) / (3 * 60) * (3 * 60) / 60, 0)
+ max_hour = max([max_weekday, max_category])
+ max_hour = dt.time((max_hour.hour * 60 + max_hour.minute) / (3 * 60) * (3 * 60) / 60 + 3, 0)
+
+
+ self.chart_weekday_starts_ends.plot_day(weekday_keys, weekdays, min_hour, max_hour)
+
+ self.chart_category_starts_ends.plot_day(category_keys, categories, min_hour, max_hour)
+
+
+ """
+ cat_activities = self._totals(facts,
+ lambda fact: "%s-%s" % (fact["name"], fact["category"]),
+ lambda fact: fact['delta'].seconds / 60)
+
+ weekdays = self._totals(facts,
+ lambda fact: fact["start_time"].weekday,
+ lambda fact: fact['delta'].seconds / 60)
+
+ dates = self._totals(facts,
+ lambda fact: fact["start_time"].day,
+ lambda fact: fact['delta'].seconds / 60)
+
+ months = self._totals(facts,
+ lambda fact: fact["start_time"].month,
+ lambda fact: fact['delta'].seconds / 60)
+ """
+
+
+
+ def _totals(self, iter, keyfunc, sumfunc):
+ """groups items by field described in keyfunc and counts totals using value
+ from sumfunc
+ """
+ data = sorted(iter, key=keyfunc)
+
+ totals = {}
+ max_total = -10000
+ for k, group in groupby(data, keyfunc):
+ totals[k] = sum([sumfunc(entry) for entry in group])
+ max_total = max(max_total, totals[k])
+
+ for total in totals: #add normalized version too
+ totals[total] = (totals[total], totals[total] / float(max_total))
+
+ return totals
def more_on_left(self):
z = min(round((self.end_date - self.start_date).days / 21.0)+1, 5)
@@ -476,7 +733,7 @@ class StatsViewer(object):
label = self.get_widget("overview_label")
- label.set_text(overview_label)
+ label.set_markup("<b>%s</b>" % overview_label)
label2 = self.get_widget("dayview_caption")
label2.set_markup("%s" % (dayview_caption))
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]