2009-12-28

TaggyTODO:工作模型(三) 任务分类

标题更新:Gotask正式更名为TaggyTODO。


我对任务分类的考虑相当简单,就一条:tag。每一个TaggyTODO中的任务都可以被打上一个或多个标记,比如“家庭事务”,“工作”,“重要”,“可以忽略”。每当定义一个新的任务时,用户可以选择打上对应的标记。在后来查询的时候我们就可以利用这些标记过滤出我们要的任务,比如“所有可以忽略的工作事务”,或“重要的家庭事务”。我知道很多管理程序都喜欢在tag之外再实现一个能包含层级关系的category概念用于分类,但我不打算这么干,因为我认为tag本身已经足够用于表示分类,至于层级关系,我认为可以用tag组合代替,不需要多此一举。

必须强调的是,我希望tag是必须先定义后使用的。这一点和通常意义上的tag不同。如今我见过的很多用tag这个概念的软件一般都是允许用户随意增减tag的,比如Blogger,每一篇文章都能随便加个标记,Blogger会记录所有用到过的标记,并只显示目前仍在被使用的。但是我认为随意定义tag的做法不适合管理任务,因为用户可能会定义一堆实际意义相同而叫法不同的标记,然后在最后定义query的时候发现自己顾此失彼,总是漏掉一两个任务。

我更期望一个tag应该是一个有限的集合。用户用之前就定义好,需要的时候从列表中选择。这样用户能更仔细地定义所有的tag,因此任务的标记信息可以更规范。除此之外,预先定义还有一个额外的好处:当用户发现某个tag名字不合理时不用一个个任务修正,只需要修改那个tag定义本身就可以了。——别忘了TaggyTODO的目标是用来管理大数量的长期任务,要求用户一个个修改任务可不是什么聪明主意。

最后,我不打算提供任务名称中关键字搜索的功能,也就是说,查询任务时tag是除时间之外唯一的搜索条件。我知道也许有人会认为这种做法太死板,特别是Outlook的用户,他们往往更习惯于在search框里输入标题中的关键字来查找。但我认为我的考虑更适合TaggyTODO,理由有两个:
  • 概念单一不会使用户养成不同的使用习惯。假定我同时提供标题字符串搜索和tag,就可能出现两个用户一个习惯用tag而另一个习惯搜索标题的情况。如果他们两人共享任务列表,则合并后的task就无法满足任何一个人的需要。
  • 基于tag查找的方法进行优化的方法简单。单纯字符串搜索就可能得动用索引结构之类的大家伙了。相比之下,我更希望我的软件能尽量小一点。
当然,TaggyTODO相对于邮件程序而言更容易实现分类规范化。因为所有的任务定义都是从用户这边来的。邮件程序必须使用字符串搜索的理由是用户收到的邮件来自四面八方,我们不可能要求所有联系人都用一样的规范写信。相比之下,都是自己定义的任务就不会有这个问题。

First Release: GoTask is renamed as TaggyTODO!

OK. So I finally pushed the first bunch of code for GoTask to Google Code. It's still incomplete, which can only run some basic unit tests, but I'll update in the new year. -- New year, new start, right? :)

Also, I finally found a name for the project: TaggyTODO. I have to change the name because I found the name GoTask has been used in another project, a library for Game development. Hope the name is not used again.

If you are interested in it, check it out from http://code.google.com/p/taggytodo.

2009-12-23

ConsoleKit, access denied and a see-it-once bug

    I just noticed an interesting bug when I upgraded my ArchLinux last Sunday.  Every time when I log on my Openbox desktop, I found my NetworkManager applet icon cannot be shown on the system tray. When I invoke nm-applet from comman line, I got an error like below:

** (nm-applet:14122): WARNING **:   applet_dbus_manager_start_service(): 
Could not acquire the NetworkManagerUserSettings service.
  Message: 'Connection ":1.21" is not allowed to own the service 
"org.freedesktop.NetworkManagerUserSettings" due to security policies in the 
configuration file'
The most interesting thing is: the issue only happens *once*. When I log out and log in again, everything looks fine.

Obviously it's not a hardware issue. Since it only happens after upgrading, I decided to have a check on udev configuration files and finally got something new:

<policy at_console="true">
   <allow own="org.freedesktop.NetworkManagerUserSettings"/>
   <allow send_destination="org.freedesktop.NetworkManagerUserSettings"
     send_interface="org.freedesktop.NetworkManagerSettings"/>
   <allow send_destination="org.freedesktop.NetworkManagerUserSettings" send_interface="org.freedesktop.NetworkManagerSettings.Connection"/>
</policy>

This is new to me. I know that the traditional way is to use network group to define who can access network configuration. I also got some suggestions from *here* that I should add to make it back to old behavior.


But wait -- is it really the root cause? If that is the case, why it works when I log on twice?

After some studies I noticed that the settings above are used by ConsoleKit. Meanwhile, I also found that there should be a daemon, /usr/sbin/console-kit-daemon, which will assign an XDG_SESSION_COOKIE environment variable to every active logon session so it can determine who is using the current console. This is important for nm-applet to determine who should be assigned the access to read network settings.

Look -- the key is here. I didn't see XDG_SESSION_COOKIE environment variable when I log on system for the first time! However, I'm able to see the setting from the second logon. It seems the server was not there but was then invoked after first session, but NO DOCUMENT says that console-kit-daemon can be automatically started!

So the final fix is rather simple: I opened my /etc/rc.local file and added two lines:
#!/bin/bash
/usr/sbin/console-kit-daemon
That will force starting a console-kit-daemon service when system starts up. I rebooted the machine and everything works then.

Also, it just solve another see-it-once issue: my PCManFM file manager always give me an error when I'm trying to mount any USB flash disk, with a message like "send message rejected". This is also a see-it-once issue on my first logon session. After applying the fix (or workaround? Who knows) above, it also disappeared.

So my friend, if you are also experiencing the same issue, try that. Good luck.

2009-11-20

Gotask:设计部分暂停

简单地说,四个原因:

a) 最近工作实在太忙,脱不开身。
b) 最近是人生一个重大转折,不可不上心。
c) Gotask可能要改名。
d) 光说不练,假把式。

这一个多月我也没闲着,目前的状态是Gotask的查询部分代码已经完成,正在开始写XML解析和数据库的部分。如今暂定的计划是年内能够发布查询和数据库部分的函数库代码,而后续的设计我希望留到至少开始UI部分再开始。

另外,Gotask使用的开发语言是Python(>= 2.5),暂时没有做C扩展的方案。目前的计划是在数据库和查询的部分实现Python2和Python3双兼容,UI部分第一个实现应当会基于GTK+。

2009-10-08

What a *Great=Fire=Wall*!

I just noticed that I haven't updated my blog for such a long time.

Now I'm in Beijing with my girlfriend (I love her, yes, I'm serious) and got a problem: I can hardly access my blog even with Tor. Thanks to the *Great=Fire=Wall*, it even blocks the global public Tor server list. :)

Fortunately, we still have bridges as the last weapon, but I'd better use it as less as possible. I'll update the blog for GoTask when I'm back to US.

To all bridges providers -- I just want to say sorry for using your bandwidth without sharing mine. This is because I'm living in a hotel right now and cannot freely use the connection.

2009-09-06

My Python Gochas: Non UTF Encoding support does not always work

    As claimed by a lot of tutorials, Python is well known to be a very good platform for manipulating XML. Yeah it's true, however there is a trap waiting for you.

Comparing with other XML APIs, such as MSXML or IBM's Xcerces-C, the default Python XML APIs (xml.dom, etree, or even PyXML) does not provide full supports for multiple encodings. For example, if you comes from China, you might want to encode your document in GB2312/GBK or BIG5. However, you might receive some information like below when you are parsing it (tried on Python 2.6.2) :

<?xml version="1.0" encoding="GB2312"?>
<hello>world</hello>

import xml.dom.minidom
xml.dom.minidom.parse('test.gbk.xml')
Traceback (most recent call last):
File "", line 1, in
File "/usr/lib/python2.6/xml/dom/minidom.py", line 1918, in parse
return expatbuilder.parse(file)
File "/usr/lib/python2.6/xml/dom/expatbuilder.py", line 924, in parse
result = builder.parseFile(fp)
File "/usr/lib/python2.6/xml/dom/expatbuilder.py", line 207, in parseFile
parser.Parse(buffer, 0)
xml.parsers.expat.ExpatError: unknown encoding: line 1, column 30

It's confusing to most of the Python newbies because the document above is actually a valid XML document. It's also surprising because python itself can correctly handle GB2312 since python 1.6.

But my friend, don't just start complaining: Python does nothing wrong.

The strange behavor comes from the library. Python uses Expat as the implementation of almost all XML libraries. Currently Expat supports very limited encodings: US-ASCII,  UTF-8,  UTF-16, and ISO-8859-1 (If you are working on Linux/UNIX, check man page of xmlwf tool). However, it is compliant with XML standard, section 2.2:

The mechanism for encoding character code points into bit patterns may vary from entity to entity. All XML processors MUST accept the UTF-8 and UTF-16 encodings of Unicode [Unicode]; the mechanisms for signaling which of the two is in use, or for bringing other encodings into play, are discussed later, in 4.3.3 Character Encoding in Entities.

As we see, XML standard requires only supports on UTF-8 and UTF-16, and XML parsers can freely make their decisions to support other encodings. Expat supports four encodings, that's all.

So, if you want to use XML with Python, please think about it carefully when choosing your default encodings. If there are requirements to support "any" encodings, don't just use Python blindly.

2009-09-04

GoTask:工作模型(二) 查询和任务管理

上一篇里我说过,GoTask的三个要素是:提醒,分类和查询。但问题是:这三者是所有的时间管理软件都必须有的,那么GoTask和我们很熟悉的Outlook或者iCal有什么区别呢?

GoTask设计的主要目的是为了支持长期任务管理,而在我看来Outlook和iCal更适合管理短期任务。所以要回答这个问题,我们不妨先看看短期任务和长期任务的差异。

在我的经验中,多数情况下短期任务往往以小时为单位计算,这一点相信每一个Outlook用户都会很熟悉:当需要定义一个会议邀请时,Outlook总是以半小时为默认单位指定时间,而一个任务一般最长也就是全天,即所谓的All Day Event。但是Outlook并不擅长显示或处理持续时间超过一天的任务——或许重复任务(Recursive Tasks)算是一种,但其实际上是可以被分解的短期任务集合。在工作中我发现,我的同事们很少会在Outlook中大量定义跨天任务。

显然,两种任务在时间安排上的差异首先会影响任务提醒功能。Outlook中的提醒虽然选择很多,但其主要目的都是通知用户某个任务将要开始。有人也许会注意到在任务开始之后Outlook实际上无法自定义提醒的频度,那是因为它假设任务开始之后总是能在短期内完成,除非用户忘记,否则没有必要多次提醒。但长期任务则不同,它常常需要对用户做多次提醒。这主要是为了便于划分任务阶段,同时也是为了能定期确定进度。如果在Outlook中要做这件事,那就得定义多个项目,然后每个项目定义单独的提醒时机。

另一个问题是,我认为每个任务单独定义提醒频率的方案不适合长期任务。因为长期任务需要的提醒时机较多,如果每个任务都必须分别制定多个提醒时间,用起来比较麻烦;而且考虑到长期任务时间安排往往较灵活,我也没有必要将提醒定得过于死板。我的习惯是每周分两次检查任务列表,同时调整接下来几天的安排。所以长期任务的提醒方式最好是能统一定义的,比如要求提醒我所有“开始日期到今天大于十天,并且尚未完成的任务”。每次系统只需要查阅数据库里所有的项目,然后找到满足要求的就可以了。


那么现在问题就变成了:我怎么才能定义这样的提醒?我的设想是通过查询来完成。换句话说,我希望提醒规则是定义于每个查询,而非每个任务中。例如,假定一个任务需要持续四个星期,而我需要第一和第三个星期提醒一次,那么我可以设定两个查询:
  1. 查找每个已经开始,并且起始日期在一周前的任务。
  2. 查找每个已经开始,并且起始日期在三周前的任务。
这样随着时间推移,我就能得到两次提醒。同样地,这个规则还可以被应用到所有其他的任务去。这种做法的好处是:如果我需要修正提醒的时机或频率,只需修改查询的定义,而后所有工作任务的提醒规则都能相应地更新。在长期任务数量巨大的情况下,这种组织方式比Outlook的方案更容易管理。

这种设计在程序上还有一个好处:每个查询结果可以被再次利用。我可以用查询来构造一个简单的统计系统。比如我想知道过去两个星期里所有完成的任务占总任务的比例,我需要定义两个查询表达式,在得到查询的结果后做一个简单的百分比,就能得到我需要的数据。

最后,在我的设想中这种设计也应当能够在一定程度上处理短期任务,但需要一个前提:GoTask的时间计量必须支持到小时或分钟。这样我就能通过定义一个检查“开始时间在当前15分钟之后”的查询来模拟Outlook的行为了。


现在我们还剩下最后一个问题:分类。我将它留到下一篇中讨论。

2009-08-29

GoTask: 工作模型(一):功能

  上一篇中我解释了GoTask应用的场合,那么现在我们自然而然地就有了第二个问题:我们该如何用GoTask管理任务呢?

要回答这个问题,我需要研究一下我自己的工作。对我来说,我每天需要花在任务管理上的精力主要都用来完成两件事:
  1. 分类:根据各种标准对任务进行区别。
  2. 排序:按事情完成的先后关系决定完成次序。
有意思的是,我发现在工作中我很少需要对任务做排序。也许是因为我手头的任务往往繁琐而杂乱,任务之间彼此联系很少,又或者是老板已经规定了各个工作阶段。总之,事实上我很少有必须对手头任务排序的机会。另外,我发现自己同时追踪多个任务时效率很有限,远不如一个接一个地串行完成便于控制。所以多数情况下我只需要做个分类,剩下的事情就简单地按照时间先后定序即可。

同时,我对分类的功能要求却很高。比如我常常需要同时和别的组的多个进行任务交接,这时我就需要把任务根据接洽对象的不同分为几类,然后对比每个交接工作的安排是否有所缺失。但与此同时,我也需要根据时间段对任务做划分,这样我才能知道大概在某一段时间里我需要做多少任务。有时候我同时接到很多任务,这时候我就得根据什么时候完成而它们分成几部分,而不能管他们从何处而来。

谈到了时间我们就必须提到一个重要功能,也就是提醒。当我们需要管理的任务很多时,每个人都会或多或少忘记一些事情。这时候工具适时发出的提醒就显得非常重要。

最后一个我需要的功能是查询。当任务数目太多的时候,单靠人去记忆显然有些勉为其难,既然我们已经对任务进行了分类,那么考虑使用某种形式的查询显然就是水到渠成的事情。

如此我就得到了GoTask必须支持的三个功能:
  1. 任务分类:可以根据使用者的标准不同自行定义。
  2. 任务提醒:在任务开始或终止时通知用户。
  3. 任务查询:每一个任务应该能通过某种方式查询其时间和分类。
这其中分类本身包含两个用途:第一,它是显示的一部分,使我能得知当前任务的性质;第二,它是查询条件的一部分,使我能过滤掉一部分任务,从而关注最想关心的东西。

这样,每当我得到一个新任务,我就可以在GoTask里增加一个项目,然后根据情况打一到多个标记作为分类。我可以每天早晨查询一次,确定今天必须完成的任务,然后每天下班时重新查询一次,看看有多少还没有完成。当然,如果到了设定的时间,GoTask应该提醒我任务将至,应该早做准备。


看到这里或许大家会觉得我只是给自己重新画了个Outlook,因为Outlook也有任务,而且提醒、分类、查询一个也不少。别急,我正在考虑工作模型的下一个部分:复杂条件查询和长期任务跟踪。我们下次再说。

2009-08-25

GoTask: 设想

想写这个东西已经很久了。这一年多以来忙得昏天黑地,我这个念头只能一直埋在自己心里,从来没有真正付诸实践。一个月前有一次无意中和东哥聊起,突然发现这个念头就像一个心里的结,总是挥之不去。最重要的是,我越来越想试试能不能实现它。

OK,那么我们回到正题。首先,什么是GoTask?

GoTask是我想做的一个小软件,用来管理自己的个人工作事务。我希望它可以支持这样一些功能:
  1. 以天为单位跟踪每天的工作项目列表。
  2. 支持以Tag为基础的历史搜索。
  3. 支持统计功能。
  4. 支持不同工作项目的合并和检索。
在我的设想中,GoTask应该被设计为管理这样一些任务的:第一,它们需要一段较长的时间完成,比如几天甚至几个星期;第二,它们没有详细到每个小时的计划,只有大概的每日进度估计;第三,它们总是有明确的起始和结束时间。

这么古怪的需求很大程度上和我的工作有关。因为工作中我总是处在这样一个环境里:每天任务随时随地出现,其中大部分都不很紧急但比较耗时,我可以选择在几天甚至几周内完成。另外最重要的是:这些事我肯定无法全部做完。所以我每天必须做的一件事就是取舍,有些确实可以不做的,就不管了;有的重要但不需要自己处理的,就交给别人;而有些则必须自己完成。同时,由于一部分任务几乎总是会被放弃,我希望我的工具能帮我做一些统计工作,比如它能分门别类地告诉我,在过去的三个星期内我的总任务有多少,做完的和放弃的比例有多少等等。

有人也许想问:为什么不用Outlook的Calendar?因为我认为Outlook更适合管理当天开始并完成的短任务,以小时为单位。而我想要的是个能管理那些无明确计划的任务的工具。基于同样的理由,东哥强力推荐的iCal也给否了。另一个抛弃Outlook的理由是:它几乎没有统计功能。而这是我想做这么一个软件的主要理由。

其实有一个软件在统计功能上非常接近我的要求,Hamster,可惜后来研究了一番之后还是觉得它不是我想要的。主要的问题有两个:首先,它的界面更倾向于提醒那些当天必须做完的事情;其次,它的搜索功能弱了些。除此之外还有一个小瑕疵:它是个Gnome Applet,而我的系统里没有Gnome。

所以我觉得也许我需要自己动手。今天先写到这里,具体的细节以后慢慢讨论。

关于重开Blog的说明

终于开始认真地决定要用这个Blog了。

说起来,这个Blog从注册开始也过了三年,可我从来没认真用过。原因相信是众所周知的,由于一些大家心照不宣的问题,在国内的网络环境里要想畅通无阻地访问Blogger实在是困难重重。虽然对我自己而言这从来都不是问题,好歹自己也是搞软件的,可既然开了一个Blog,总是希望大家都能读到。加上后来有一段时间基本上只用Hotmail和Live Messenger,自然比较倾向于集成做得更好的Live Spaces,这几年下来,几乎把这边给忘记了。

但是这几年Live Spaces用得多了,却发现自己越来越不喜欢她。过于花哨而不实用的界面或许是一个因素,而真正的原因则有些难以启齿:太不自由了。为什么?因为我的Live Messenger上公司的同事太多,很多人都会跟着连到我的Blog上,这时要自由地对公司内外的技术发表评论难免有些束手束脚。也许这只是我个人的片面印象,但我在北京时,不少我敬重的同事对别家技术的了解程度似乎总是与我所预期相去甚远;反过来,他/她们对MS自家的技术却总有一种毫无来由的狂热。每当和别人争论的时候,他/她们都会言之凿凿地向每一个听众论证MS的每一项新技术是如何优秀;但恕我不恭,有时我发现这些技术他/她们自己甚至都没有体验过。这种感觉在我来到Redmond一年之后更加明显:我所认识的许多Redmond的同事们私下里从不避讳任何对公司产品缺陷的评论。

熟悉我的朋友们都知道,由于出身的原因,我总是更倾向于关注MS之外,特别是Open Source世界的一些动向,而且最要命的是我喜欢顺嘴说几句自己公司的坏话。为此我的一位大学好友兼MS Fan甚至直接把《冰与火之歌》中“忧郁的艾迪”这一名头送给了我,评曰:勤勤恳恳的乌鸦嘴。无论这种习惯是好是坏,在Live Spaces这种众目睽睽的环境下我发现自己无法毫无顾忌地写出对很多事情的真实看法,更不可能持续我对Open Source的关注。


回到主题。既然我已经决定重新启用这个Blog,那么总要做一些变化。无论如何,我希望在这个Blog里更多地讨论一些我自己喜欢的话题,偏重技术。这其中包括MS的东西,更包括很多Open Source的内容。这里是我个人的Blog,我也不是MS的新闻发布官。虽然维护公司形象也是分内的事,但也犯不着处处往公司脸上贴金,只是希望说一些真实的个人看法。


至于语言呢,基本上还是英文和中文,中文为主。毕竟是母语,表述起来也比较得心应手。不过有时候我也会用英文换换口味,特别是很多技术术语无法找到合适的中文表达的时候。

最后,关于国内访问的问题,我确实是无能为力了。只是作为一个著名的乌鸦嘴,我心里多少还存了点心思。如果可以的话,我希望能坚持到能从国内顺畅无阻地访问Blogger的那一天。