[kupfer: 1/7] +plugin.gtg: plugin for Getting Things GNOME! (GTG)



commit c78fc73e3e1fcb4a0e95db26821643488ec8c09b
Author: Karol BÄ?dkowski <karol bedkowski gmail com>
Date:   Fri May 7 22:27:20 2010 +0200

    +plugin.gtg: plugin for Getting Things GNOME! (GTG)
    
    Plugin provide:
    - access to tasks in GTG (list in Kupfer, open in GTG)
    - create new task from TextLeaf
    - change status of Task to complited, dismiss and delete
    
    When GTG is not running tasks are loading from xml files
    stored in ~/.local/share/gtg/. Also new tasks are created by
    call gtg_new_task application.
    
    When GTG is available (and dbus connection can be established) all
    data are loaded by dbus. Unfortunately GTG don't provide any
    "on-data-changes" signal, so all updates are detected by detect changes in
    xml files.

 kupfer/plugin/gtg.py |  248 ++++++++++++++++++++++++++++++++++++++++++++++++++
 po/POTFILES.in       |    1 +
 2 files changed, 249 insertions(+), 0 deletions(-)
---
diff --git a/kupfer/plugin/gtg.py b/kupfer/plugin/gtg.py
new file mode 100644
index 0000000..b0c4c6b
--- /dev/null
+++ b/kupfer/plugin/gtg.py
@@ -0,0 +1,248 @@
+# -*- coding: UTF-8 -*-
+__kupfer_name__ = _("Getting Things GNOME")
+__kupfer_sources__ = ("TasksSource", )
+__kupfer_actions__ = ("CreateNewTask",)
+__description__ = _("Browse and create new task in GTG")
+__version__ = "2010-05-07"
+__author__ = "Karol BÄ?dkowski <karol bedkowski gmail com>"
+
+
+import os
+import subprocess
+from xml.etree import cElementTree as ElementTree
+
+import dbus
+import gio
+
+from kupfer import plugin_support
+from kupfer import pretty
+from kupfer.obj.base import Leaf, Action, Source
+from kupfer.obj.objects import TextLeaf
+from kupfer.obj.apps import AppLeafContentMixin
+from kupfer.obj.helplib import PicklingHelperMixin
+
+plugin_support.check_dbus_connection()
+
+_SERVICE_NAME = 'org.GTG'
+_OBJECT_NAME = '/org/GTG'
+_IFACE_NAME = 'org.GTG'
+
+
+def _create_dbus_connection(activate=False):
+	''' Create dbus connection to Gajim
+		@activate: true=starts gajim if not running
+	'''
+	interface = None
+	sbus = dbus.SessionBus()
+	try:
+		proxy_obj = sbus.get_object('org.freedesktop.DBus',
+				'/org/freedesktop/DBus')
+		dbus_iface = dbus.Interface(proxy_obj, 'org.freedesktop.DBus')
+		if activate or dbus_iface.NameHasOwner(_IFACE_NAME):
+			obj = sbus.get_object(_SERVICE_NAME, _OBJECT_NAME)
+			if obj:
+				interface = dbus.Interface(obj, _IFACE_NAME)
+	except dbus.exceptions.DBusException, err:
+		pretty.print_debug(err)
+	return interface
+
+
+def _load_tasks(interface):
+	''' Load task by dbus interface '''
+	for task in interface.get_tasks():
+		title = task['title'] or task['text'][:80]
+		otask = Task(task['id'], title, task['status'])
+		otask.duedate = task['duedate']
+		otask.startdate = task['startdate']
+		otask.tags = task['tags']
+		yield otask
+
+
+def _load_task_from_xml():
+	''' Load tasks by xml file (when no gtg running) '''
+	gtg_local_dir = os.path.expanduser("~/.local/share/gtg/")
+	for fname in os.listdir(gtg_local_dir):
+		if not fname.endswith('.xml') or fname == 'projects.xml' or \
+				fname == 'tags.xml':
+			continue
+		ffullpath = os.path.join(gtg_local_dir, fname)
+		if not os.path.isfile(ffullpath):
+			continue
+		tree = ElementTree.parse(ffullpath)
+		for task in tree.findall('task'):
+			status = task.attrib['status']
+			if status != 'Active':
+				continue
+			task_id = task.attrib['id']
+			title = task.find('title').text.strip()
+			otask = Task(task_id, title, status)
+			tags = task.attrib['tags']
+			if tags:
+				otask.tags = tags.split(",")
+			duedate_n = task.find('duedate')
+			if duedate_n is not None:
+				otask.duedate = duedate_n.text.strip()
+			startdate_n = task.find('startdate')
+			if startdate_n is not None:
+				otask.startdate = startdate_n.text.strip()
+			yield otask
+
+
+class Task(Leaf):
+	def __init__(self, task_id, title, status):
+		Leaf.__init__(self, task_id, title)
+		self.status = status
+		self.tags = None
+		self.duedate = None
+		self.startdate = None
+
+	def get_description(self):
+		descr = [self.status]
+		if self.duedate:
+			descr.append(_("due: %s") % self.duedate)
+		if self.startdate:
+			descr.append(_("start: %s") % self.startdate)
+		if self.tags:
+			descr.append(_("tags: %s") % " ".join(self.tags))
+		return "  ".join(descr)
+
+	def get_icon_name(self):
+		return 'gtg'
+
+	def get_actions(self):
+		yield OpenTaskEditor()
+		yield DeleteTask()
+		yield MarkTaskDone()
+		yield DismissTask()
+
+
+class OpenTaskEditor(Action):
+	def __init__(self):
+		Action.__init__(self, _("Open Task Editor"))
+
+	def activate(self, leaf):
+		interface = _create_dbus_connection()
+		if interface is not None:
+			interface.open_task_editor(leaf.object)
+
+	def get_icon_name(self):
+		return 'gtk-open'
+
+
+class DeleteTask(Action):
+	rank_adjust = -5
+
+	def __init__(self):
+		Action.__init__(self, _("Delete Task"))
+
+	def activate(self, leaf):
+		interface = _create_dbus_connection()
+		if interface is not None:
+			interface.delete_task(leaf.object)
+
+	def get_icon_name(self):
+		return 'delete'
+
+	def get_description(self):
+		return _("Permanently remove this task")
+
+
+class MarkTaskDone(Action):
+	def __init__(self):
+		Action.__init__(self, _("Mark Task Done"))
+
+	def activate(self, leaf):
+		interface = _create_dbus_connection()
+		if interface is not None:
+			task = interface.get_task(leaf.object)
+			task['status'] = 'Done'
+			interface.modify_task(leaf.object, task)
+
+	def get_icon_name(self):
+		return 'gtg-task-done'
+
+	def get_description(self):
+		return _("Mark this task as done")
+
+
+class DismissTask (Action):
+	def __init__(self):
+		Action.__init__(self, _("Dismiss Task"))
+
+	def activate(self, leaf):
+		interface = _create_dbus_connection()
+		if interface is not None:
+			task = interface.get_task(leaf.object)
+			task['status'] = 'Dismiss'
+			interface.modify_task(leaf.object, task)
+
+	def get_icon_name(self):
+		return 'gtg-task-dismiss'
+
+	def get_description(self):
+		return _("Mark this task as not to be done anymore")
+
+
+class CreateNewTask(Action):
+	def __init__(self):
+		Action.__init__(self, _("Create New Task"))
+
+	def activate(self, leaf):
+		interface = _create_dbus_connection()
+		if interface is not None:
+			if '\n' in leaf.object:
+				title, text = leaf.object.split('\n', 1)
+				interface.open_new_task(title, text)
+			else:
+				interface.open_new_task(leaf.object, '')
+		else:
+			p = subprocess.Popen(["gtg_new_task", "-i"], stdin=subprocess.PIPE,
+					close_fds=True)
+			p.stdin.write(leaf.object)
+			p.stdin.close()
+
+	def item_types(self):
+		yield TextLeaf
+
+	def get_icon_name(self):
+		return 'gtg-task-new'
+
+	def get_description(self):
+		return _("Create new task in Getting Things GNOME")
+
+
+class TasksSource(AppLeafContentMixin, Source, PicklingHelperMixin):
+	appleaf_content_id = 'gtg'
+
+	def __init__(self, name=_('GTG Tasks')):
+		Source.__init__(self, name)
+		self._tasks = []
+		self._version = 2
+
+	def initialize(self):
+		gtg_local_dir = os.path.expanduser("~/.local/share/gtg/")
+		gfile = gio.File(gtg_local_dir)
+		self.monitor = gfile.monitor_directory(gio.FILE_MONITOR_NONE, None)
+		if self.monitor:
+			self.monitor.connect("changed", self._changed)
+
+	def pickle_prepare(self):
+		self.monitor = None
+
+	def get_items(self):
+		interface = _create_dbus_connection()
+		self._tasks = []
+		if interface is None:
+			self._tasks = list(_load_task_from_xml())
+		else:
+			self._tasks = list(_load_tasks(interface))
+		return self._tasks
+
+	def get_icon_name(self):
+		return 'gtg'
+
+	def provides(self):
+		return Task
+
+	def _changed(self, _monitor, _file1, _file2, _evt_type):
+		self.mark_for_update()
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 04cc1ed..b067649 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -77,6 +77,7 @@ kupfer/plugin/gmail/__init__.py
 kupfer/plugin/google_picasa/__init__.py
 kupfer/plugin/google_search.py
 kupfer/plugin/google_translate.py
+kupfer/plugin/gtg.py
 kupfer/plugin/higherorder.py
 kupfer/plugin/image.py
 kupfer/plugin/kupfer_plugins.py



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]