[hamster-applet] warning - unstable if not unusable! splitting up whole thing in server (storage & db backend) and cl
- From: Toms Baugis <tbaugis src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [hamster-applet] warning - unstable if not unusable! splitting up whole thing in server (storage & db backend) and cl
- Date: Tue, 13 Apr 2010 01:58:39 +0000 (UTC)
commit 8dc9596d8b130da56477c4ffd3670bf97bfba9b4
Author: Toms Bauģis <toms baugis gmail com>
Date: Mon Apr 12 22:33:13 2010 +0100
warning - unstable if not unusable! splitting up whole thing in server (storage & db backend) and client (the rest) and communicating via a wakeup daemon.
.gitignore | 1 +
Makefile.am | 19 ++-
bye-hamster.sh | 1 +
configure.ac | 2 -
org.gnome.hamster.service.in | 3 +
src/Makefile.am | 22 +--
src/{hamster-applet.py => hamster-applet} | 0
src/{hamster-client.py => hamster-client} | 10 +-
src/hamster-service | 40 +++
src/hamster-standalone | 19 --
src/hamster/Makefile.am | 2 +-
src/hamster/applet.py | 20 +--
src/hamster/client.py | 193 +++++++++++++++
src/hamster/configuration.py | 4 +-
src/hamster/db.py | 39 ++--
src/hamster/hamsterdbus.py | 290 ----------------------
src/hamster/overview_totals.py | 7 +-
src/hamster/storage.py | 375 ++++++++++++++++++++++-------
tests/hamsterdbus_test.py | 169 -------------
19 files changed, 581 insertions(+), 635 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index ba0b051..796d2eb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,3 +20,4 @@ autom4te.cache
m4
gnome-doc-utils.make
hamster-applet-*.tar.gz
+org.gnome.hamster.service
diff --git a/Makefile.am b/Makefile.am
index e703d85..e2116d8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3,6 +3,15 @@ ACLOCAL_AMFLAGS = -I m4
DISTCHECK_CONFIGURE_FLAGS = --disable-scrollkeeper
+# Dbus service file
+servicedir = $(datadir)/dbus-1/services
+nodist_service_DATA = org.gnome.hamster.service
+
+org.gnome.hamster.service: org.gnome.hamster.service.in
+ sed -e s!\ prefix\@!$(prefix)! < $< > $@
+org.gnome.hamster.service: Makefile
+
+
release: dist
scp $(PACKAGE)-$(VERSION).tar.gz tbaugis master gnome org:
ssh tbaugis master gnome org install-module $(PACKAGE)-$(VERSION).tar.gz
@@ -12,10 +21,16 @@ DISTCLEANFILES = \
intltool-merge \
intltool-update
+CLEANFILES = org.gnome.hamster.service
EXTRA_DIST = \
MAINTAINERS \
TODO \
intltool-extract.in \
intltool-merge.in \
- intltool-update.in
- gnome-doc-utils.make
+ intltool-update.in \
+ gnome-doc-utils.make \
+ org.gnome.hamster.service.in
+
+
+
+all-local: org.gnome.hamster.service
diff --git a/bye-hamster.sh b/bye-hamster.sh
index 3171b85..d75042e 100755
--- a/bye-hamster.sh
+++ b/bye-hamster.sh
@@ -10,3 +10,4 @@ sudo rm -Rf /usr/lib/python2.6/dist-packages/hamster/
sudo rm -Rf /usr/local/lib/python2.6/dist-packages/hamster/
sudo rm -Rf /usr/local/lib/python2.6/site-packages/hamster/
sudo rm -Rf /usr/share/gnome/help/hamster-applet/
+sudo rm -f /usr/bin/hamster-*
diff --git a/configure.ac b/configure.ac
index 66fcdbd..18cd08e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -144,8 +144,6 @@ except:
fi
fi
-
-
dnl ==========================
dnl Control-Center Keybindings
dnl ==========================
diff --git a/org.gnome.hamster.service.in b/org.gnome.hamster.service.in
new file mode 100644
index 0000000..f0bb9f2
--- /dev/null
+++ b/org.gnome.hamster.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.gnome.Hamster
+Exec= prefix@/bin/hamster-service
diff --git a/src/Makefile.am b/src/Makefile.am
index 4da980c..16331ed 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -4,14 +4,6 @@ ACLOCAL_AMFLAGS = -I m4
CPPFLAGS = \
$(PYTHON_INCLUDES)
-hamster-applet: hamster-applet.py
- $(AM_V_GEN)sed -e "s|\ PYTHONDIR\@|$(pyexecdir)|" $< > $@
-
-hamster-client: hamster-client.py
- $(AM_V_GEN)sed -e "s|__DEBUG__ = True|__DEBUG__ = False|" \
- -e "s|\ PYTHONDIR\@|$(pyexecdir)|" \
- $< > $@
-
hamsterlibdir = $(libdir)/hamster-applet
hamsterlib_SCRIPTS = hamster-applet
@@ -20,20 +12,14 @@ hamsterbindir = $(bindir)
hamsterbin_SCRIPTS = \
hamster-standalone \
gnome-time-tracker \
- hamster-client
-
-BUILT_SOURCES = \
- hamster-applet \
- hamster-client
-
-CLEANFILES = \
- $(BUILT_SOURCES)
+ hamster-client \
+ hamster-service
DISTCLEANFILES = \
$(CLEANFILES)
EXTRA_DIST = \
- hamster-applet.py \
+ hamster-applet \
hamster-standalone \
gnome-time-tracker \
- hamster-client.py
+ hamster-client
diff --git a/src/hamster-applet.py b/src/hamster-applet
similarity index 100%
rename from src/hamster-applet.py
rename to src/hamster-applet
diff --git a/src/hamster-client.py b/src/hamster-client
similarity index 98%
rename from src/hamster-client.py
rename to src/hamster-client
index 4893f65..072f5c6 100755
--- a/src/hamster-client.py
+++ b/src/hamster-client
@@ -25,7 +25,7 @@ import sys, os
import optparse
import re
import locale, gettext
-import time
+from calendar import timegm
import datetime as dt
import dbus
@@ -111,7 +111,7 @@ class HamsterClient(object):
len(headers['tags'])
print "%s+%s" % ('-' * first_column_width, '-' * second_column_width)
- for fact in self.conn.GetFacts(start_stamp, end_stamp):
+ for fact in self.conn.GetFacts(start_stamp, end_stamp, ""):
if fact['start_time'] < start_stamp or fact['start_time'] > end_stamp:
# Hamster returns activities for the whole day, not just the
# time range we sent
@@ -140,8 +140,7 @@ class HamsterClient(object):
def timestamp_from_datetime(date):
'''Convert a local time datetime into an utc timestamp.'''
if date:
- tz_offset = dt.timedelta(seconds=time.timezone)
- return time.mktime((date-tz_offset).timetuple())
+ return timegm(date.timetuple())
else:
return 0
@@ -321,6 +320,3 @@ Time formats:
elif command == 'list-categories':
hamster_client.list_categories()
-
-
-
diff --git a/src/hamster-service b/src/hamster-service
new file mode 100755
index 0000000..49c95ff
--- /dev/null
+++ b/src/hamster-service
@@ -0,0 +1,40 @@
+#!/usr/bin/python
+# nicked off gwibber
+
+import sys, optparse, gobject, dbus
+from os.path import join, dirname, exists, realpath, abspath
+from os import popen, getpid
+from dbus.mainloop.glib import DBusGMainLoop
+
+DBusGMainLoop(set_as_default=True)
+loop = gobject.MainLoop()
+
+# if gwibber-serivce is already running, don't start
+if "org.gnome.Hamster.Connection" in dbus.SessionBus().list_names():
+ print "Found hamster-service already running, exiting"
+ quit()
+
+
+LAUNCH_DIR = abspath(sys.path[0])
+SOURCE_DIR = LAUNCH_DIR
+STORAGE = join(SOURCE_DIR, "hamster", "storage.py")
+
+######################################################################
+# Setup path
+if exists(SOURCE_DIR):
+ sys.path.insert(0, realpath(dirname(SOURCE_DIR)))
+ try:
+ from hamster import db
+ finally:
+ del sys.path[0]
+
+else:
+ from hamster import db
+
+#TODO - add db monitor (or maybe do that in db itself)
+
+
+print "Starting up service..."
+
+storage = db.Storage(loop)
+loop.run()
diff --git a/src/hamster-standalone b/src/hamster-standalone
index 14c7d7e..7ff890a 100755
--- a/src/hamster-standalone
+++ b/src/hamster-standalone
@@ -33,7 +33,6 @@ from hamster import eds
from hamster.configuration import conf, runtime, dialogs
from hamster import stuff, keybinder
-from hamster.hamsterdbus import HAMSTER_URI, HamsterDbusController
# controllers for other windows
from hamster import widgets
@@ -85,9 +84,6 @@ class ProjectHamster(object):
# DBus Setup
try:
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
- name = dbus.service.BusName(HAMSTER_URI, dbus.SessionBus())
- self.dbusController = HamsterDbusController(bus_name = name)
-
# Set up connection to the screensaver
self.dbusIdleListener = idle.DbusIdleListener(runtime.dispatcher)
runtime.dispatcher.add_handler('active_changed', self.on_idle_changed)
@@ -110,9 +106,6 @@ class ProjectHamster(object):
self.last_activity = None
self.load_day()
- # Hamster DBusController current fact initialising
- self.__update_fact()
-
# refresh hamster every 60 seconds to update duration
gobject.timeout_add_seconds(60, self.refresh_hamster)
@@ -288,17 +281,6 @@ class ProjectHamster(object):
self.window.present()
- def __update_fact(self):
- """dbus controller current fact updating"""
- last_activity_id = 0
-
- if not self.last_activity:
- self.dbusController.TrackingStopped()
- else:
- last_activity_id = self.last_activity['id']
-
- self.dbusController.FactUpdated(last_activity_id)
-
def _delayed_display(self):
"""show window only when gtk has become idle. otherwise we get
mixed results. TODO - this looks like a hack though"""
@@ -356,7 +338,6 @@ class ProjectHamster(object):
def after_fact_update(self, event, date):
self.load_day()
- self.__update_fact()
def on_idle_changed(self, event, state):
# state values: 0 = active, 1 = idle
diff --git a/src/hamster/Makefile.am b/src/hamster/Makefile.am
index 0a3b9c1..acfeec9 100644
--- a/src/hamster/Makefile.am
+++ b/src/hamster/Makefile.am
@@ -24,7 +24,7 @@ hamster_PYTHON = \
edit_activity.py \
applet.py \
about.py \
- hamsterdbus.py \
+ client.py \
idle.py \
i18n.py \
pytweener.py \
diff --git a/src/hamster/applet.py b/src/hamster/applet.py
index 35962a6..323ffd9 100755
--- a/src/hamster/applet.py
+++ b/src/hamster/applet.py
@@ -1,6 +1,6 @@
# - coding: utf-8 -
-# Copyright (C) 2007-2009 Toms Bauģis <toms.baugis at gmail.com>
+# Copyright (C) 2007-2010 Toms Bauģis <toms.baugis at gmail.com>
# Copyright (C) 2007-2009 Patryk Zawadzki <patrys at pld-linux.org>
# Copyright (C) 2008 PÄ?teris Caune <cuu508 at gmail.com>
@@ -35,7 +35,6 @@ from configuration import conf, runtime, dialogs
import stuff
import keybinder
-from hamsterdbus import HAMSTER_URI, HamsterDbusController
# controllers for other windows
import widgets
@@ -225,9 +224,6 @@ class HamsterApplet(object):
# DBus Setup
try:
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
- name = dbus.service.BusName(HAMSTER_URI, dbus.SessionBus())
- self.dbusController = HamsterDbusController(bus_name = name)
-
# Set up connection to the screensaver
self.dbusIdleListener = idle.DbusIdleListener(runtime.dispatcher)
runtime.dispatcher.add_handler('active_changed', self.on_idle_changed)
@@ -251,8 +247,6 @@ class HamsterApplet(object):
self.load_day()
self.update_label()
- # Hamster DBusController current fact initialising
- self.__update_fact()
# refresh hamster every 60 seconds to update duration
gobject.timeout_add_seconds(60, self.refresh_hamster)
@@ -447,17 +441,6 @@ class HamsterApplet(object):
fact = self.treeview.get_selected_fact()
runtime.storage.remove_fact(fact["id"])
- def __update_fact(self):
- """dbus controller current fact updating"""
- last_activity_id = 0
-
- if not self.last_activity:
- self.dbusController.TrackingStopped()
- else:
- last_activity_id = self.last_activity['id']
-
- self.dbusController.FactUpdated(last_activity_id)
-
def __show_toggle(self, event, is_active):
"""main window display and positioning"""
self.button.set_active(is_active)
@@ -584,7 +567,6 @@ class HamsterApplet(object):
def after_fact_update(self, event, date):
self.load_day()
self.update_label()
- self.__update_fact()
def on_idle_changed(self, event, state):
# state values: 0 = active, 1 = idle
diff --git a/src/hamster/client.py b/src/hamster/client.py
new file mode 100644
index 0000000..e420b7f
--- /dev/null
+++ b/src/hamster/client.py
@@ -0,0 +1,193 @@
+# - coding: utf-8 -
+
+# Copyright (C) 2007 Patryk Zawadzki <patrys at pld-linux.org>
+# Copyright (C) 2007-2009 Toms Baugis <toms baugis gmail com>
+
+# This file is part of Project Hamster.
+
+# Project Hamster 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.
+
+# Project Hamster 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 Project Hamster. If not, see <http://www.gnu.org/licenses/>.
+
+
+import datetime as dt
+from calendar import timegm
+import dbus, dbus.mainloop.glib
+
+def debus(value):
+ """recasts dbus types to the basic ones. should be quite an overhead"""
+
+ if isinstance(value, dbus.Array):
+ return [debus(val) for val in value]
+
+ elif isinstance(value, dbus.Dictionary):
+ return dict([(debus(key), debus(val)) for key, val in value.items()])
+
+ elif isinstance(value, unicode):
+ return unicode(value)
+ elif isinstance(value, int):
+ return int(value)
+ elif isinstance(value, bool):
+ return bool(value)
+
+ return value
+
+def from_dbus_fact(fact):
+ fact['start_time'] = dt.datetime.utcfromtimestamp(fact['start_time'])
+ if fact['end_time']:
+ fact['end_time'] = dt.datetime.utcfromtimestamp(fact['end_time'])
+ else:
+ fact['end_time'] = None
+
+ if 'date' in fact:
+ fact['date'] = dt.datetime.utcfromtimestamp(fact['date']).date()
+
+ if 'delta' in fact:
+ days, seconds = divmod(fact['delta'], 24 * 60 * 60)
+ fact['delta'] = dt.timedelta(days = days, seconds = seconds)
+
+ return fact
+
+class Storage(object):
+ def __init__(self, parent):
+ self.parent = parent
+
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+ bus = dbus.SessionBus()
+ hamster_conn = dbus.Interface(bus.get_object('org.gnome.Hamster',
+ '/org/gnome/Hamster'),
+ dbus_interface='org.gnome.Hamster')
+ self.conn = hamster_conn
+
+ bus.add_signal_receiver(self.on_tags_changed, 'TagsChanged', 'org.gnome.Hamster')
+ bus.add_signal_receiver(self.on_facts_changed, 'FactsChanged', 'org.gnome.Hamster')
+ bus.add_signal_receiver(self.on_activities_changed, 'ActivitiesChanged', 'org.gnome.Hamster')
+
+
+ def on_tags_changed(self):
+ self.parent.dispatch('new_tags_added')
+
+ def on_facts_changed(self):
+ self.parent.dispatch('day_updated')
+
+ def on_activities_changed(self):
+ self.parent.dispatch('activity_updated')
+
+
+ def get_todays_facts(self):
+ return [from_dbus_fact(fact) for fact in self.conn.GetTodaysFacts()]
+
+ def get_facts(self, date, end_date = 0, search_terms = ""):
+ date = timegm(date.timetuple())
+ if end_date:
+ end_date = timegm(end_date.timetuple())
+
+ return [from_dbus_fact(fact) for fact in self.conn.GetFacts(date,
+ end_date,
+ search_terms)]
+
+
+ def get_autocomplete_activities(self, search = ""):
+ return self.conn.GetAutocompleteActivities(search)
+
+ def get_category_list(self):
+ return self.conn.GetCategories()
+
+ def get_tags(self, autocomplete = None):
+ return self.conn.GetTags(True)
+
+
+ def get_tag_ids(self, tags):
+ return self.conn.GetTagIds(tags)
+
+ def update_autocomplete_tags(self, tags):
+ self.conn.SetTagsAutocomplete(tags)
+
+ def get_fact(self, id):
+ return from_dbus_fact(self.conn.GetFact(id))
+
+ def add_fact(self, activity_name, tags, start_time = None, end_time = 0,
+ category_name = '', description = ''):
+
+ if start_time:
+ start_time = timegm(start_time).timetuple()
+ else:
+ start_time = 0
+
+ if end_time:
+ end_time = timegm(end_time.timetuple())
+ else:
+ end_time = 0
+
+ return self.conn.AddFact(activity_name, tags, start_time, end_time, category_name, description)
+
+ def touch_fact(self, fact, end_time = None):
+ # TODO - rename and remove all the attributes
+ return self.conn.StopTracking()
+
+ def remove_fact(self, fact_id):
+ self.conn.RemoveFact(fact_id)
+
+ def update_fact(self, fact_id, activity_name, tags, start_time = 0, end_time = 0, category_name = '', description = ''):
+ category_name = category_name or '' # so to override None
+ description = description or '' # so to override None
+
+ if start_time:
+ start_time = timegm(start_time.timetuple())
+ if end_time:
+ end_time = timegm(end_time.timetuple())
+ else:
+ end_time = 0
+
+ return self.conn.UpdateFact(fact_id, activity_name, tags, start_time, end_time, category_name, description)
+
+
+ def get_activities(self, category_id = None):
+ return self.conn.GetActivities(category_id)
+
+
+ def get_last_activity(self):
+ return self.conn.GetLastActivity()
+
+ def remove_activity(self, id):
+ self.conn.RemoveActivity(id)
+
+ def remove_category(self, id):
+ self.conn.RemoveCategory(id)
+
+ def move_activity(self, source_id, target_order, insert_after = True):
+ self.conn.MoveActivity(source_id, target_order, insert_after)
+
+ def change_category(self, id, category_id):
+ self.conn.ChangeCategory(id, category_id)
+
+ def swap_activities(self, id1, priority1, id2, priority2):
+ self.conn.SwapActivities(id1, priority1, id2, priority2)
+
+ def update_activity(self, id, name, category_id):
+ return self.conn.UpdateActivity(id, name, category_id)
+
+ def add_activity(self, name, category_id = -1):
+ return self.conn.AddActivity(name, category_id)
+
+ def update_category(self, id, name):
+ return self.conn.UpdateCategory(id, name)
+
+ def add_category(self, name):
+ return self.conn.AddCategory(name)
+
+
+ def get_category_by_name(self, category):
+ return self.conn.GetCategoryByName(category)
+
+ def get_activity_by_name(self, activity, category_id = None, ressurect = True):
+ return self.conn.GetActivityByName(activity, category_id, ressurect)
diff --git a/src/hamster/configuration.py b/src/hamster/configuration.py
index 864fc8f..278cb69 100644
--- a/src/hamster/configuration.py
+++ b/src/hamster/configuration.py
@@ -26,7 +26,7 @@ import gconf
import gettext
import os
import defs
-from db import Storage
+from client import Storage
from dispatcher import Dispatcher
from xdg.BaseDirectory import xdg_data_home
import logging
@@ -96,7 +96,7 @@ class RuntimeStore(Singleton):
return
logging.info("DB file has been modified externally. Calling all stations")
- self.storage.dispatch_overwrite()
+ #self.storage.dispatch_overwrite()
diff --git a/src/hamster/db.py b/src/hamster/db.py
index fb01c47..ce73ffa 100644
--- a/src/hamster/db.py
+++ b/src/hamster/db.py
@@ -334,7 +334,8 @@ class Storage(storage.Storage):
last_activity = facts[-1]
return last_activity
- def __touch_fact(self, fact, end_time):
+ def __touch_fact(self, fact):
+ end_time = dt.datetime.now()
# tasks under one minute do not count
if end_time - fact['start_time'] < datetime.timedelta(minutes = 1):
self.__remove_fact(fact['id'])
@@ -470,7 +471,7 @@ class Storage(storage.Storage):
tags = [tag.strip() for tag in tags.split(",") if tag.strip()] # split by comma
tags = tags or activity.tags # explicitly stated tags take priority
- tags = self.get_tag_ids(tags) #this will create any missing tags too
+ tags = self.GetTagIds(tags) #this will create any missing tags too
if category_name:
@@ -480,14 +481,28 @@ class Storage(storage.Storage):
start_time = activity.start_time or start_time or datetime.datetime.now()
- if start_time > datetime.datetime.now():
- return None #no facts in future, please
-
start_time = start_time.replace(microsecond = 0)
end_time = activity.end_time or end_time
if end_time:
end_time = end_time.replace(microsecond = 0)
+ now = datetime.datetime.now()
+ # if in future - roll back to past
+ if start_time > datetime.datetime.now():
+ start_time = dt.datetime.combine(now.date(), start_time.time())
+ if start_time > now:
+ start_time -= dt.timedelta(days = 1)
+
+ if end_time and end_time > now:
+ end_time = dt.datetime.combine(now.date(), end_time.time())
+ if end_time > now:
+ end_time -= dt.timedelta(days = 1)
+
+
+
+
+ print activity, start_time, end_time
+
if not start_time or not activity.activity_name: # sanity check
return
@@ -710,18 +725,6 @@ class Storage(storage.Storage):
return res
- def __get_popular_categories(self):
- """returns categories used in the specified interval"""
- query = """
- SELECT coalesce(c.name, ?) as category, count(a.id) as popularity
- FROM facts a
- LEFT JOIN activities b on a.activity_id = b.id
- LEFT JOIN categories c on c.id = b.category_id
- GROUP BY b.category_id
- ORDER BY popularity desc
- """
- return self.fetchall(query, (_("Unsorted"), ))
-
def __remove_fact(self, fact_id):
statements = ["DELETE FROM fact_tags where fact_id = ?",
"DELETE FROM facts where id = ?"]
@@ -732,7 +735,7 @@ class Storage(storage.Storage):
otherwise - by activity_order"""
if category_id:
query = """
- SELECT a.*, b.name as category
+ SELECT a.id, a.name, a.activity_order, a.category_id, b.name as category
FROM activities a
LEFT JOIN categories b on coalesce(b.id, -1) = a.category_id
WHERE category_id = ?
diff --git a/src/hamster/overview_totals.py b/src/hamster/overview_totals.py
index 9a0e98c..6bcb6a4 100644
--- a/src/hamster/overview_totals.py
+++ b/src/hamster/overview_totals.py
@@ -191,9 +191,10 @@ class TotalsBox(gtk.VBox):
# tag totals
tag_sums = {}
for fact in facts:
- for tag in fact["tags"]:
- tag_sums.setdefault(tag, 0)
- tag_sums[tag] += fact["delta"].seconds + fact["delta"].days * 24 * 60 * 60
+ if fact["tags"]:
+ for tag in fact["tags"]:
+ tag_sums.setdefault(tag, 0)
+ tag_sums[tag] += fact["delta"].seconds + fact["delta"].days * 24 * 60 * 60
for entry in tag_sums:
tag_sums[entry] = tag_sums[entry] / 60 / 60.0
diff --git a/src/hamster/storage.py b/src/hamster/storage.py
index 0c6ceb8..9ab4f37 100644
--- a/src/hamster/storage.py
+++ b/src/hamster/storage.py
@@ -18,142 +18,347 @@
# You should have received a copy of the GNU General Public License
# along with Project Hamster. If not, see <http://www.gnu.org/licenses/>.
+import dbus, dbus.service
+import datetime as dt
+from calendar import timegm
-import datetime
+def to_dbus_fact(fact):
+ """Perform the conversion between fact database query and
+ dbus supported data types
+ """
+ if not fact:
+ return dbus.Dictionary({}, signature='sv')
+
+ fact = dict(fact)
+ for key in fact.keys():
+ fact[key] = fact[key] or 0
+
+ # make sure we return correct type where strings are expected
+ if not fact[key] and key in ('name', 'category', 'description'):
+ fact[key] = ''
+
+ # convert times to gmtime
+ if isinstance(fact[key], dt.datetime) or isinstance(fact[key], dt.date):
+ fact[key] = timegm(fact[key].timetuple())
+ elif isinstance(fact[key], dt.timedelta) :
+ fact[key] = fact[key].days * 24 * 60 * 60 + fact[key].seconds
+ return fact
+
+
+class Storage(dbus.service.Object):
+ __dbus_object_path__ = "/org/gnome/Hamster"
+
+ def __init__(self, loop):
+ self.bus = dbus.SessionBus()
+ bus_name = dbus.service.BusName("org.gnome.Hamster", bus=self.bus)
+ dbus.service.Object.__init__(self, bus_name, self.__dbus_object_path__)
+ self.mainloop = loop
-class Storage(object):
- def __init__(self, parent):
- self.parent = parent
def run_fixtures(self):
pass
- def dispatch(self, event, data = None):
- self.parent.dispatch(event, data)
+ @dbus.service.signal("org.gnome.Hamster")
+ def TagsChanged(self): pass
+
+ @dbus.service.signal("org.gnome.Hamster")
+ def FactsChanged(self): pass
+
+ @dbus.service.signal("org.gnome.Hamster")
+ def ActivitiesChanged(self): pass
def dispatch_overwrite(self):
- self.dispatch('new_tags_added')
- self.dispatch('day_updated')
- self.dispatch('activity_updated')
+ self.TagsChanged()
+ self.FactsChanged()
+ self.ActivitiesChanged()
- def get_tags(self, autocomplete = None):
- return self.__get_tags(autocomplete)
- def get_tag_ids(self, tags):
- tags, new_added = self.__get_tag_ids(tags)
- if new_added:
- self.dispatch('new_tags_added')
- return tags
- def update_autocomplete_tags(self, tags):
- changes = self.__update_autocomplete_tags(tags)
- if changes:
- self.dispatch('new_tags_added')
+ @dbus.service.method("org.gnome.Hamster")
+ def Quit(self):
+ """
+ Shutdown the service
+ example:
+ import dbus
+ obj = dbus.SessionBus().get_object("org.gnome.Hamster", "/org/gnome/Hamster")
+ service = dbus.Interface(obj, "org.gnome.Hamster")
+ service.Quit()
+ """
+ #log.logger.info("Hamster Service is being shutdown")
+ self.mainloop.quit()
+
- def get_fact(self, id):
- return self.__get_fact(id)
+ # facts
- def add_fact(self, activity_name, tags, start_time = None, end_time = None,
+ @dbus.service.method("org.gnome.Hamster", in_signature='ssiiss', out_signature='i')
+ def AddFact(self, activity_name, tags, start_time, end_time,
category_name = None, description = None):
+ if start_time:
+ start_time = dt.datetime.utcfromtimestamp(start_time)
+ else:
+ start_time = None
+
+ if end_time:
+ end_time = dt.datetime.utcfromtimestamp(end_time)
+ else:
+ end_time = None
+
+ print activity_name, tags, start_time, end_time, category_name, description
self.start_transaction()
result = self.__add_fact(activity_name, tags, start_time, end_time, category_name, description)
self.end_transaction()
if result:
- self.dispatch('day_updated')
+ print "emiting factschanged"
+ self.FactsChanged()
return result
- def touch_fact(self, fact, end_time = None):
- end_time = end_time or datetime.datetime.now()
- result = self.__touch_fact(fact, end_time)
- self.dispatch('day_updated', fact['start_time'])
+
+ @dbus.service.method("org.gnome.Hamster", in_signature='i', out_signature='a{sv}')
+ def GetFact(self, fact_id):
+ """Gets the current displaying fact
+ Parameters:
+ i id: Unique fact identifier
+ Returns Dict of:
+ i id: Unique fact identifier
+ s name: Activity name
+ s category: Category name
+ s description: Description of the fact
+ u start_time: Seconds since epoch (timestamp)
+ u end_time: Seconds since epoch (timestamp)
+ as tags: List of tags used
+ """
+ return to_dbus_fact(self.__get_fact(fact_id))
+
+
+ @dbus.service.method("org.gnome.Hamster", in_signature='issiiss', out_signature='i')
+ def UpdateFact(self, fact_id, activity_name, tags, start_time, end_time, category_name = None, description = None):
+ if start_time:
+ start_time = dt.datetime.utcfromtimestamp(start_time)
+ else:
+ start_time = None
+
+ if end_time:
+ end_time = dt.datetime.utcfromtimestamp(end_time)
+ else:
+ end_time = None
+
+
+ self.start_transaction()
+ self.__remove_fact(fact_id)
+ result = self.__add_fact(activity_name, tags, start_time, end_time, category_name, description)
+ self.end_transaction()
+
+ if result:
+ self.FactsChanged()
return result
- def get_facts(self, date, end_date = None, search_terms = ""):
- return self.__get_facts(date, end_date, search_terms)
- def get_todays_facts(self):
- return self.__get_todays_facts()
+ @dbus.service.method("org.gnome.Hamster")
+ def StopTracking(self):
+ """Stops the current fact tracking"""
+ fact = self.__get_last_activity()
+ if fact:
+ self.__touch_fact(fact)
+ self.FactsChanged()
- def get_popular_categories(self):
- return self.__get_popular_categories()
- def remove_fact(self, fact_id):
+ @dbus.service.method("org.gnome.Hamster", in_signature='i')
+ def RemoveFact(self, fact_id):
self.start_transaction()
- fact = self.get_fact(fact_id)
+ fact = self.__get_fact(fact_id)
if fact:
self.__remove_fact(fact_id)
- self.dispatch('day_updated', fact['start_time'])
+ self.FactsChanged()
self.end_transaction()
- def update_fact(self, fact_id, activity_name, tags, start_time, end_time, description = None):
- self.start_transaction()
- self.__remove_fact(fact_id)
- result = self.__add_fact(activity_name, tags, start_time, end_time, description = description)
- self.end_transaction()
- if result:
- self.dispatch('day_updated')
- return result
+ @dbus.service.method("org.gnome.Hamster", in_signature='uus', out_signature='aa{sv}')
+ def GetFacts(self, start_date, end_date, search_terms):
+ """Gets facts between the day of start_date and the day of end_date.
+ Parameters:
+ u start_date: Seconds since epoch (timestamp). Use 0 for today
+ u end_date: Seconds since epoch (timestamp). Use 0 for today
+ s search_terms: Bleh
+ Returns Array of fact where fact it's Dict of:
+ i id: Unique fact identifier
+ s name: Activity name
+ s category: Category name
+ s description: Description of the fact
+ u start_time: Seconds since epoch (timestamp)
+ u end_time: Seconds since epoch (timestamp)
+ as tags: List of tags used
+ """
+ #TODO: Assert start > end ?
+ if start_date:
+ start = dt.datetime.utcfromtimestamp(start_date).date()
+ else:
+ start = dt.date.today()
+
+ if end_date:
+ end = dt.datetime.utcfromtimestamp(end_date).date()
+ else:
+ end = dt.date.today()
+
+ facts = self.__get_facts(start, end, search_terms)
+ return [to_dbus_fact(fact) for fact in facts]
+
+
+ @dbus.service.method("org.gnome.Hamster", out_signature='aa{sv}')
+ def GetTodaysFacts(self):
+ """Gets facts of today, respecting hamster midnight.
+ Returns Array of fact where fact it's Dict of:
+ i id: Unique fact identifier
+ s name: Activity name
+ s category: Category name
+ s description: Description of the fact
+ u start_time: Seconds since epoch (timestamp)
+ u end_time: Seconds since epoch (timestamp)
+ as tags: List of tags used
+ """
+
+ facts = dbus.Array([], signature='a{sv}')
+
+ for fact in self.__get_todays_facts():
+ facts.append(to_dbus_fact(fact))
+
+ return facts
+
+
+ # categories
+
+ @dbus.service.method("org.gnome.Hamster", in_signature='s', out_signature = 'i')
+ def AddCategory(self, name):
+ res = self.__add_category(name)
+ self.ActivitiesChanged()
+ return res
+
+ @dbus.service.method("org.gnome.Hamster", in_signature='s', out_signature='a{sv}')
+ def GetCategoryByName(self, category):
+ return dict(self.__get_category_by_name(category))
+
+ @dbus.service.method("org.gnome.Hamster", in_signature='is')
+ def UpdateCategory(self, id, name):
+ self.__update_category(id, name)
+ self.ActivitiesChanged()
+
+
+ @dbus.service.method("org.gnome.Hamster", in_signature='i')
+ def RemoveCategory(self, id):
+ self.__remove_category(id)
+ self.ActivitiesChanged()
+
+
+ @dbus.service.method("org.gnome.Hamster", out_signature='aa{sv}')
+ def GetCategories(self):
+ res = []
+ for category in self.__get_category_list():
+ category = dict(category)
+ category['color_code'] = category['color_code'] or ''
+ res.append(category)
+
+
+ return res
- def get_activities(self, category_id = None):
- return self.__get_activities(category_id = category_id)
- def get_autocomplete_activities(self, search = ""):
- return self.__get_autocomplete_activities(search)
+ # activities
- def get_last_activity(self):
- return self.__get_last_activity()
+ @dbus.service.method("org.gnome.Hamster", in_signature='si', out_signature = 'i')
+ def AddActivity(self, name, category_id = -1):
+ new_id = self.__add_activity(name, category_id)
+ self.ActivitiesChanged()
+ return new_id
- def remove_activity(self, id):
+
+ @dbus.service.method("org.gnome.Hamster", in_signature='isi')
+ def UpdateActivity(self, id, name, category_id):
+ self.__update_activity(id, name, category_id)
+ self.ActivitiesChanged()
+
+
+
+ @dbus.service.method("org.gnome.Hamster", in_signature='i')
+ def RemoveActivity(self, id):
result = self.__remove_activity(id)
- self.dispatch('activity_updated')
+ self.ActivitiesChanged()
return result
- def remove_category(self, id):
- self.__remove_category(id)
- self.dispatch('activity_updated')
+ @dbus.service.method("org.gnome.Hamster", out_signature='a{sv}')
+ def GetLastActivity(self):
+ """Gets the current displaying fact
+ Returns Dict of:
+ i id: Unique fact identifier
+ s name: Activity name
+ s category: Category name
+ s description: Description of the fact
+ u start_time: Seconds since epoch (timestamp)
+ u end_time: Seconds since epoch (timestamp)
+ u end_time: Seconds since epoch (timestamp)
+ as tags: List of tags used
+ """
+ return to_dbus_fact(self__.get_last_activity())
+
+
+ @dbus.service.method("org.gnome.Hamster", in_signature='i', out_signature='aa{sv}')
+ def GetActivities(self, category_id = None):
+ print self.__get_activities(category_id = category_id)
+ return [dict(activity) for activity in self.__get_activities(category_id = category_id)]
+
+
+
+ @dbus.service.method("org.gnome.Hamster", in_signature='s', out_signature='aa{sv}')
+ def GetAutocompleteActivities(self, search = ""):
+ res = []
+ for activity in self.__get_autocomplete_activities(search):
+ activity = dict(activity)
+ activity['category'] = activity['category'] or ''
- def move_activity(self, source_id, target_order, insert_after = True):
+ return res
+
+
+
+ @dbus.service.method("org.gnome.Hamster", in_signature='iib')
+ def MoveActivity(self, source_id, target_order, insert_after = True):
self.__move_activity(source_id, target_order, insert_after)
- self.dispatch('activity_updated')
+ self.ActivitiesChanged()
+
- def change_category(self, id, category_id):
+ @dbus.service.method("org.gnome.Hamster", in_signature='ii')
+ def ChangeCategory(self, id, category_id):
changed = self.__change_category(id, category_id)
if changed:
- self.dispatch('activity_updated')
- return changed
+ self.ActivitiesChanged()
- def swap_activities(self, id1, priority1, id2, priority2):
- res = self.__swap_activities(id1, priority1, id2, priority2)
- self.dispatch('activity_updated')
- return res
- def update_activity(self, id, name, category_id):
- self.__update_activity(id, name, category_id)
- self.dispatch('activity_updated')
+ @dbus.service.method("org.gnome.Hamster", in_signature='iiii')
+ def SwapActivities(self, id1, priority1, id2, priority2):
+ self.__swap_activities(id1, priority1, id2, priority2)
+ self.ActivitiesChanged()
- def add_activity(self, name, category_id = -1):
- new_id = self.__add_activity(name, category_id)
- self.dispatch('activity_updated')
- return new_id
- def update_category(self, id, name):
- self.__update_category(id, name)
- self.dispatch('activity_updated')
+ @dbus.service.method("org.gnome.Hamster", in_signature='sib', out_signature='a{sv}')
+ def GetActivityByName(self, activity, category_id = None, ressurect = True):
+ return dict(self.__get_activity_by_name(activity, category_id, ressurect))
- def add_category(self, name):
- res = self.__add_category(name)
- self.dispatch('activity_updated')
- return res
- def get_category_list(self):
- return self.__get_category_list()
+ # tags
+ @dbus.service.method("org.gnome.Hamster", in_signature='b', out_signature='aa{sv}')
+ def GetTags(self, autocomplete = None):
+ return [dict(tag) for tag in self.__get_tags(autocomplete)]
- def get_category_by_name(self, category):
- return self.__get_category_by_name(category)
- def get_activity_by_name(self, activity, category_id = None, ressurect = True):
- return self.__get_activity_by_name(activity, category_id, ressurect)
+ @dbus.service.method("org.gnome.Hamster", in_signature='as', out_signature='aa{sv}')
+ def GetTagIds(self, tags):
+ tags, new_added = self.__get_tag_ids(tags)
+ if new_added:
+ self.TagsChanged()
+ return [dict(tag) for tag in tags]
+
+
+ @dbus.service.method("org.gnome.Hamster", in_signature='as')
+ def SetTagsAutocomplete(self, tags):
+ changes = self.__update_autocomplete_tags(tags)
+ if changes:
+ self.TagsChanged()
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]