您正在查看 "dev & design" 分类下的文章
2007-05-08 11:07
冲动的结果 - BGMail!
长长的五一假期, 我在做什么? 除了洗衣做饭脱地板傻吃闷睡之外, 我的大部分时间都在神情郁闷的写程序, 为了一个冲动的决定......
应该是在五一放假的前一天, 我终于觉得我不能再忍受 Thunderbird 了 (2.0), 用它实在无法有效的管理每天几百封的邮件. 比如, 如果我用 threaded 列表,一封新收到的邮件可能是一个历史久远 (也就一两天, 但是已经能排好几页了)的 thread, 那么 thunderbird 没有一个排序方法能够让这个有新邮件的thread 排到最前; 另外对打了 tag 或 star 的邮件, 我想在只看 tag 和 star的列表里看到这样的邮件后, 再点去查看整个 thread 的上下文 --- 没有任何办法. 另外还有一些 ``小'' 问题, 比如, 要加个 filter, 你不能选定把信过滤发到一个不存在的目录 (expect 它在提示后自动创建), 在那个界面也米有可以创建目录的控制, 所以我每次设 filter 都是开 filter - 建规则 - 发现没目录, 退出 filter 界面跑去建目录, 再来一遍.
我搜过 thunderbird 的 addons, 也用过 evolution 和 kmail (这个我用了几年了), 发现没有一个办法能完全满足我现在的需要. 可能是最近一年被 GMail 惯坏了, 我还是习惯于 GMail 的信息管理方式, 但是用 GMail 去管理内部邮件是不现实的. 我去用 GMail-like 搜索了看是不是有类似 GMail 的 mail client, 结果除了 sf 上一两个处于原始开发阶段的项目外, 一无所获.
所以我的决定是: 利用五一假期, 写一个个人版的 GMail, 这就是 BGMail 的来历, 由于这是我个人制造个人使用, 所以能保证它完全满足我的需要 (不满足的时候再自己改就行了), 还能提供一些根本无法对普通用户提供的功能. BGMail的 意思就是 BeyondGMail, 产品的好坏是由用户决定的, 对于我这个用户来说,我相
信 BGMail 能做到这一点. 当然 BGMail 也可以是 BG Me 啊, Big Gmail = 大猪猫之类啦, 哈哈.
这个冲动的决定令我非常兴奋, 所以我整个五一假期都在拼命的写程序. 其实也许一些console-based 的 mail client, 如 mutt, 再加上一砣砣的扩展, 也许能基本满足我的需要, 但是, 相比于痛苦的配置, 完全按照自己的意愿去写自己的软件,并且在有新需求的时候能自己随意扩展, 不是更有趣的事情吗?
我计划在五一期间完成的并且现在已经实际达到的功能点如下.
1. 类似 GMail 的 tag-based 邮件管理, 同样有 star 功能.
2. 与 GMail 不同的 UI 细节: 对于有未读邮件的 tag, 点的时候不是跳到完整的浏览界面,而是直接在列表界面下拉浏览未读的邮件, 这样能节省阅读大量简短的新邮件的时间. 记忆中 GMail 也有类似的油猴子扩展. 打 tag 的方法不像 GMail 而更像 GReader 的 in place 输入. GMail 的那种标 label 的方法更照顾了对tag-based 管理方式不熟悉的普通用户, 但对我来说是太过烦琐了.
3. 阅读界面能够像 GMail 一样将 quoted text 隐藏 (我就奇怪, 为什么 thunderbird, outlook 等等就没有一个能在 GMail 之前想到这个这么自然的功能呢)
4. 纯文本的 composer, 支持附件. (我本来就很讨厌收到 HTML 邮件, 发邮件当然都是纯文本)
5. auto-filter (在这里可能应该叫 auto-tagger), 可以用一种简单的基于正则匹配的脚本语言对一个邮件的 tag 进行细致的控制 (It's all for geeeeeeks!)
6. 管理方式和 GMail 的不同: star 是对 thread 而不是对单个邮件的; 另外如果邮件被标了 tag, 它就不会在 Inbox 里出现, 这个比较接近于传统 folder-based 的管理. 因为如果像 GMail 的 Inbox 一样, 我的 Inbox 一天就可能上十页, 就根本起不到作用了.
我计划在下一个有较多空闲的时间段完成的功能点有:
1. 高效率的全文检索, 支持将检索结果设为一个目录/tag.
2. 键盘快捷键控制.
截图欠奉, 因为发现模糊实际邮件信息之后整个界面就是一个大色块. 大概就是 GMail 的简装版啦, 没什么特别的.
虽说这是一个冲动的决定, 但我在做计划的时候还是充分考虑了可行性的. 事实上主要功能的完成时间比计划中还提早了一天. 我的信息来自我使用的一大串强大的工具: python, berkeleyDB, web.py, cjson, lighttpd, 还有在我的毕设 solomon 中抽取出来的 RPC 协议. 这些工具使我在短短 5 天, python + javascript 代码量不到 3000 行的规模里, 完成了 BGMail 的基本功能.
基本功能完成后, 在实际使用中也发现了一些问题.
1. In-place 的未读邮件浏览: 这确实能提高查看邮件的效率. 但它的逻辑是这样的: 如果一个 thread 有未读, 则 in-place 浏览, 如果没有未读, 跳到完整浏览界面. 虽然有未读邮件的 thread 会有粗体, 但连我自己有时侯都会迷惑: 在同样的情况下 (仅字体不同) 同样的操作会有完全不同的结果. 现在正在想如何改进这个流程.
2. 基于 thread 而不是基于邮件的 star. 这主要是为了解决 GMail 在邮件列表中打星星会产生的疑惑: 你不知道这时候是哪个邮件, 或是整个 thread 被打邮件了, 要取消星星是系统的行为更是无法预测 (说实话 GMail 也很疑惑, 它不知道你想干什么). 所以在 BGMail 里就干脆直接给 thread 打星. 但这也同样有问
题: 有时侯我们是希望在长长的灌水列表中给某一两个重要信息打星的, 而 BGMail 无法实现这一点, 你点开打星的thread 之后还要自己慢慢看. 我想 GMail 和 BGMail 都同样可以考虑这个逻辑:在列表里打星的时候, 给 thread 里的所有邮件都打星, 但同时也支持对单个邮件打星. 但这样列表上对只有一部分邮件打了星的邮件的呈现还是一个令人疑惑的问题, 如果还是像现在这样显示, 用户取消星星时系统的行为, 就会跟现在一样令人费解了.
3. 分 tag 浏览的问题: 在一个 tag 的列表中, 点一个邮件, 它会给出整个 thread 中邮件的列表, 即使那个邮件没有打上这个 tag. 这在很多时候符合我们的期望 (要知道 thread 的上下文), 但有时侯我们确实只想看这个 thread 中, 标上了这个 tag 的邮件, BGMail 无法实现这个功能. 似可以在界面中加上只显示某个 tag 的下拉选择. 上面所述的 star 功能也同理. (更新: 此功能已基本实现) |
2007-02-10 11:24
如果在搜藏里收藏一个 RSS, 快照就会变成介个样子:
http://cang.baidu.com/kyhpudding/snap/229f8eed30aa1a09de5d9aad.html
就是 RSS Feed 直接当 HTML 转文本的后果......
如果可以允许搜藏 RSS feed, 那就应该:
1. 能够解析 RSS Feed, 能在点击时给出下拉列表, 显示内容
2. 在 RSS 有更新时, 给用户提示
3. 在搜索时能对 RSS 中的每个项目单独索引
另一个问题是, 很大一部分用户根本不知道 RSS 是什么回事, 就更别说搜藏 RSS 了.
所以, 一个与之相应的能力是: 当用户搜藏一个网页, 而网页上有指明 RSS 信息的, 应该让这条搜藏同时可以看到快照和在下拉列表浏览最新内容, 在出现更新时也可以提醒用户
最后, 一个跟 RSS 无关的咚咚. 如果用户在搜藏时选择 ``提示更新'', 应该在网页有更新时, 提醒用户.
这些做法的最终目的都是提高用户粘性, 让用户可以持续关住他搜藏的东西, 而不仅仅是需要的时候来找一找. 搜藏的东西也不是一个静态的网页, 而是不断更新的内容, 而且有条件的时候可以结构化地清晰地显示. 当然引入这些功能会令搜藏复杂很多, 它不仅仅是一个网络书签, 它还是一个 RSS 阅读器, 要将这些功能结合起来, 提供清晰, 简单, 易懂的用户界面, 也是一个很有挑战性的问题. |
2007-01-10 11:29
强烈地晕......
因为通讯协议中有一个操作是必须传一个 int 作请求, 然后对端回传一个 int 应答才能继续的 (一个要求 keep alive 的操作). 于是当然就涉及了 no delay 的问题, 不然速度会很夸张
我用的是这两句
val = 1;
setsockopt(sock, SOL_TCP, TCP_NODELAY, &val, sizeof(val));
val = 0;
setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &val, sizeof(val));
把 client 端 sock, server 的监听 sock, 连接 sock 都设了 no delay
结果速度还是很慢, 再去看看设完 SO_SNDBUF 的参数, 要么就跟没设一样, 要么就跟我设定的不同, 或者取的时候就出错, 郁闷 to death, 不知道怎么回事呢, 因为 setsockopt 是没有返回错的....... |
2006-10-19 02:52
发信人: kyhpudding (上帝周末加班), 信区: Linux 标 题: 我的 emacs-wiki 之旅 发信站: 逸仙时空 Yat-sen Channel (Thu Oct 19 02:05:53 2006), 转信
刚才 iWater 在 Programming 版提到了 emacs-wiki, 正好激发了我写作的兴趣 . emacs-wiki 是一个好咚咚, 值得推荐给大家.
什么是 emacs-wiki
emacs-wiki 是一个 emacs 扩展, 是一个 wiki 模式的网页/笔记撰写工具. 特 别适合具有奇怪的发散性思维的人士使用.
ps. 我使用的 emacs-wiki 是一个很老的版本, 和新版本最主要的差别是没有多 个 project 的支持, 但因此配置和使用也简单了很多, 所以一直就懒得换了.
给个截图, ps. 这是 emacs23. 我在 linux 和 windows 版本的 emacs21, 22, 23 上都使用过 emacs-wiki, 没有任何兼容问题.
WIKI 适合我吗?
比之现在流行的 Blog, WIKI 特别适合具有奇怪的发散性思维的人使用, 无论你 的思维多么混乱, 多么天马行空, 尽管开启和编辑你的 WIKI 页面, 把一切记录 下来, 不用受任何限制. 通常 WIKI 的格式控制都是 "大标题, 小标题, 列表 " 这一类的逻辑性控制, 而不是 Blog 的那种所见即所得的格式控制. 这特别 适合组织大文章, 我常常用 emacs-wiki 来写文章草稿.
与 Blog 不同, WIKI 组织页面的方法不靠时间排序也不靠 tag, 而是靠的一种连 接. WIKI 适合喜欢做笔记而不是写日记的人用, 每个 WIKI 页面都是一个主题, 通过链接把众多页面组织起来, 翻找资料非常方便.
现在我的 emacs-wiki 的页面应该已经超过 80 个, 虽然有的极少使用, 但里面 记录的东西可能那天还会被我翻出来使用.
emacs-wiki 适合我吗?
- 首先, 如果你是一个熟练的 emacs 使用者, 正在找一个好的组织网页和笔记的 工具, 那 emacs-wiki 当然是上上之选.
- 另外, 如果你非常习惯乃至享受键盘操作, 也不讨厌没有所见即所得的界面, 使用 emacs-wiki 应该会有很高的编辑效率; 如果你认为腾出手来按鼠标会打 断你行云流水的思维, 那么 emacs-wiki 可能是你唯一的选择. 即使你不熟悉 emacs, 应该也能很快熟悉并熟练的运用这个强大的编辑器以及 emacs-wiki, 确实不乏因为emacs-wiki 而学习 emacs 的人.
- 如果, 以上都不是. 我非常推荐你使用 Tiddy Wiki, 一个在单个网页上实现的 WIKI, 简单易用, 无须任何服务端支持, 也可以上传发布, 而且界面非常漂亮 . 可惜我试用了它大概一个星期, 发现还是无法忍受频繁的鼠标操作, 放弃.
基本操作
怎么安装我就不说了, 会用 emacs 的都肯定会安装.
安装好, 打开一个 emacs-wiki 页面 (M-x emacs-wiki-find-file), 默认页面 是 WelcomePage, 然后, 就开始写吧.
一开始只要知道两个规则: 文章里留空一行是分段, 默认的页面名字必须是 "WelcomePage" 这样的 "驼峰命名", 即名字里没有空格, 有两个或以上分 开的大写字母 (其实通常就是两个单词的组合). 在你的文章里写这样的名字, emacs-wiki 会自动地为你建立链接, 所以你就只要一直写下去就行了, 不用理 会页面怎么连接之类的事情.
一些更复杂的格式可以看 emacs-wiki 的文档. 其实这个页面已经使用了大部分常用的格式和控制功能了 . 其实大家一看就能明白, 马上就能自己开写了.
个性化配置
除非必要, 一般的配置工作. 不推荐自己编辑 .emacs 文件. 但其实我也不知道 emacs-wiki 的配置菜单到底藏在哪里了. 我通常就是 Options - Customize Emacs - Groups Match Regexp, 然后输入 emacs-wiki 就可以了. 这里能修改 emacs-wiki 的操作界面风格, 更重要的是能定制输出文件的各种细节, 在 Emacs Wiki Publishing 组里.
各种配置项应该一看就能明白了. 特别有用的是 Emacs Wiki Publishing Header 和 Footer 项, 他们配置 emacs-wiki 生成的 HTML 文件头和文件尾的 内容, 这全部是可以直接填的. 里面还可以使用 <lisp> 标签加入 elisp 语句实现更丰富的输出, 具 体见相关文档.
要让自己的 WIKI 页面更加漂亮, 与众不同, 一个重要的手段就是定制自己的 CSS. google 一下也能找到很多给 emacs-wiki 用的 CSS, 找个差不多的再按自 己的心意稍加修改就能用了.
与 emacs 结合
- 要编辑一大堆页面, 当然需要一个 tabber 扩展啦, 它能在 emacs 编辑区顶 部显示页面标签, 并可以定制快捷键自由切换, 我绑在 win + 方向键上了, 这个键在 linux 里属于不用白不用类型, 咔咔.
- 记得开启自动折行 (fill-mode). 这对长篇大论的排版非常有用, 我这篇文章 的格式完全是自动排的, 没有手工控制.
- outline-mode 结合 emacs-wiki 来用也是很多人的选择. 但我不大喜欢我的 内容哗的就只变成一行的样子. 其实适当调整各个小标题的显示格式, 看起来 就已经很清爽了.
我的增强工具
使用 emacs-wiki 的过程中, 我也做了几个方便的小工具.
自动上传 wiki
以下两个脚本能方便地把 wiki 上传到 student.zsu.edu.cn 的空间上去.
pubwiki.sh
#!/bin/sh genrss gen_jsdata lftp -f ~/bin/upload_script.lftp
upload_script.lftp
open student.zsu.edu.cn user 用户名 密码 cd public_html mirror --reverse ~/doc/wiki/publish
第一个脚本的第三行就是调用下面的 lftp 脚本把 wiki 页面上传, 那么上面的 两个是什么呢? 就是下面的两个独一无二的功能, 咔咔.
给 emacs-wiki 提供 RSS Feed
谁说只有 Blog 才有 RSS Feed 的? 自己动手写了几十行 python 程序, 就让 emacs-wiki 也有 RSS 了. 不过这个功能现在还不很完善, 而且处理速度比较慢 . 源代码在 这里. 然后写个类似这样的脚本调用它就可以了.
#!/bin/sh echo "Generating RSS Files..." for x in `ls /home/pudding/doc/wiki/`; do wikirss.py $x > /home/pudding/doc/wiki/publish/rss/$x.xml done
然后需要设置以下 emacs-wiki 输出的文件头和文件尾, 把相应的链接加上去 . 这个可以参考我的主页的网页源码.
emacs-wiki 上的全文搜索
为了让 emacs-wiki 在没有服务器支持的情况下支持全文搜索, 研究了好久弄出 来的咚咚. 效果可以看我的主页. 介绍文章可以在 Programming 版找找, 下载 在 这里. 但架设安装不是一般的麻烦, 下次再写, 有兴趣的同学也可以联系我. |
2006-08-13 13:16
背景
我的主页 http://student.zsu.edu.cn/~is03kyh 有约 80 个页面, 用
emacs-wiki 组织. 纯文本总量在 500K 左右. 找一个特定页面可能要点好多次
才能找到, 甚至连我自己有时候要找回年代较为久远的笔记时也要找个半天. 所
以需要一个全文搜索引擎.
本来是想用嵌入 google 或百度的搜索条来做的, 但我的主页排名太低, 一些页
面经常搜索不到. 所以要想办法自己解决, 然而 student.zsu.edu.cn 是一个静
态网页服务器, 想加入自己的服务模块也是不可能的, 这就催生了用 AJAX 技术
完全在浏览器端实现全文搜索引擎的想法.
基本设计
在浏览器端进行搜索引擎的基本方法是这样的: 在后台向服务端发送请求, 取得
一些表示页面信息的静态文件, 对这些文件进行处理, 在这些文件中搜索, 就能
得到最后的搜索结果.
一个最简单的实现是: 有搜索请求时, 把所有文档逐个下载下来, 依次进行全文
搜索, 计算相关性后得到最终结果. 但这种方案无论在网络流量还是处理时间上
都是完全不可接受的.
一个较好的办法是: 服务端存储一系列小的 index 文件, 是每个词的出现位置
的倒排表. 并且设计一种方法, 当用户提供一个词, 我们能知道在哪个文件里拿
到它的倒排表. 那么我们只要获取有限的几个小的 index 文件, 对它们进行综
合计算就可以了. 这能大大减少网络流量和处理压力, 这是我的最基本的解决思
路.
服务端: 分词与重组
我们处理中文, 肯定要遇到的一个问题是分词. 我使用了最基本的基于动态规划
的分词方法, 一些简单的介绍见
http://googlechinablog.com/2006/04/blog-post_10.html. 词库使用了人民日
报某一年文章的词频统计和 www.nlp.org.cn 提供词库的结合. 最后出来的效果
很一般, 但已经能够满足搜索要求了.
分词后我们可以统计每个词在各个文档中出现的次数, 由此形成一个表. 表中的
每一项存储一个词在所有文档中出现的次数, 出现的每一个文档编号和出现在那
个文档的次数. 注意这里丢弃了词的位置信息, 因为如果存储这些信息的话表的
体积会变得很大, 网络传输也就可能变得吃力, 也意味着浏览器端的处理会更加
复杂和慢. 现在丢弃词的位置信息得到的搜索结果已经相当不错, 所以短期内不
会做这个改进.
之后, 我们要对这样的一个大表分块, 因为如果不分块的话浏览器端一次过下载
这么一个大表比较恐怖. 使用很简单的方法: 把词在表中的序号除32 取模就可知
道它分在哪个块. 在浏览器端我们也可以用同样的方法知道要下载哪个块的数据.
分成 32 块属于经验值, 如果分片太多, 那我们就有几千个每个几十字节的小文
件, 上传这些文件到网页服务器它会很不高兴的, 分片太多还有一个问题, 每次
搜索涉及的词都会分布在不同的片中, 我们就要去发很多个 HTTP 请求, 这样如
果同时搜索的用户很多, 对网页服务器是一个负担, 而采用较少的分片数, 虽然
单次搜索用的词在同一个片中的概率还是很小, 但是浏览器是会cache 的, 下几
次搜索就不会再去发请求向服务器要数据, 速度就会很快. 当然, 分片数太少也
是不行的, 这样每一次下载到的数据就会比较大, 那么出结果就慢. 所以有一个
权衡, 我现在的表有接近 3000 个词, 按 32 分片, 每个片在 4K 左右.
这些表是用 JSON 的方法存的, 简单地说就是每个块的文件都是一条
JavaScript 语句, 浏览器下载后执行这条 JavaScript 语句, 就能得到相应的
数据, 而且是结构化的.
服务端还要生成一个大的数据文件, 就是一开始要载入的那个
jsearch_data.js, 里面包括了文件名列表, 文件标题列表, 和一个完整的词表,
就因为这个词表, 所以这个文件比较大, 现在有近 30K. 因为没有完整的词表,
是无法通过词找到其相应的块位置的 (其实对英文可以, 对中文却不行, 这我在
下面会解释). 我现在还没有想到更好的方法解决这个问题.
浏览器端
把生成的数据传到网页服务器之后, 我们就可以开始浏览器端的讲解.
浏览器端首先要下载 jsearch_data.js. 现在我已把它改成后台读取, 第一次载
入页面会卡的问题可以部分缓解.
搜索功能的基本实现方法是对对每一个搜索词 (term), 在 jsearch_data.js 提
供的词表中遍历找到它的位置, 然后下载数据, 获得倒排表, 之后按普通的方法
计算各文档相关性即可.
当问题涉及中文的时候, 我们就涉及到这样的一个问题: 用户要搜索 "数据结
构", 这个词在服务端明显是要被分成 "数据" 和 "结构" 的, 我们要怎么
处理这样一个请求? 在浏览器端再做一次分词是很困难的, 因为中文分词需要一
个大的词频表. 我用的办法是, 在词表中搜索 "数据结构" 这个词的子集, 这
样就能找到 "数据" 这个词, 由于它的长度是 "数据结构" 的一半, 所以记
它的权重为 0.5, 对 "结构" 也是同样的处理, 然后下一步就是找 "数据"
和 "结构" 的数据就可以了. 这样的方法当然是不准确的, 但没有词的位置信
息的时候我们只能这么干, 结果出来还是可以接受的.
以上的处理也是我必须在浏览器端获得词的完整列表的原因. 如果纯英文的话,
在服务端和浏览器端用同样的方法做个 hash 就能知道它在哪个块了, 但中文由
于这个分词的问题, 我们不得不在整个词表中遍历.
于是, 我们的整个处理过程是: 对每个搜索词做上述处理, 就可以求出我们的
"任务", 即最终要下载的词表文件列表, 然后我们发起 HTTP 请求, 获得数据
并处理. 在 HTTP 请求管理方面, 我使用了串行的方法, 发完一个请求再发下一
个. 这么做的原因是 JavaScript 没有提供严格完整的线程处理机制, 并发发的
话可能有问题. 这个处理策略会是网络非常慢的地方搜索延时很长, 但经过我从
这里到教育网的奇慢无比的速度的监测, 除非查询很多个词, 否则速度还是可接
受的.
至于计算文档相关性的方法, 没什么特别, 用的 IF-IDF 方法, 即当某个 term
在 doc 出现的时候
1 + log(term 在 doc 中出现的次数) 相关度(term, doc) = --------------------------------------------- 1 + log(term 在所有文档中出现的次数)
多个词的相关度是单个词相关度的乘积.
改进计划, 扩展与移植
系统的瓶颈在于初次要下载的大词表. 如果一直保持我自己书写, 当纯文本的量
到 2-3M 的时候, 词表应该不会有太明显的增长. 但容量再上去或多人编辑的话,
词表就会膨胀, 成为不可忽视的因素. 我正在考虑如何解决这个问题.
这个系统可以用在各种静态网站上. 主要要修改的是提取网页纯文本并计算权值
的过程, 这是与每个网站的页面特点相关的. 应该也很好写.
浏览器端的代码可以直接在网页上找到. 服务端代码连同词库在
http://student.zsu.edu.cn/~is03kyh/publish/jsearch_server.tgz. 仅作技
术探讨之用, 请勿用于其他目的. |
|
| |