俺收到了这封信:
SETI@home [email protected]
发件人当地时间 发送时间 17:24 (GMT-07:00)。发送地当前时间:上午6:17。 ✆
回复 [email protected]
主题 SETI@home Needs Your Help
Dear Zoom.Quiet:
Green Bank Telescope In the last year, SETI@home has made great steps forward thanks to the generosity of volunteers like you. Earlier this year we were able to use the Green Bank Telescope (pictured) in West Virginia to expand our search to include the nearly 100 potentially habitable planets found by the Kepler Mission. We've also re-observed seventy two of the 102 sources of unidentified radio pulses found with our Astropulse search. We hope to observe the remaining sources soon. While it's likely that these pulses are a natural phenomenon, we don't yet know what we will find. Data from both these projects have arrived or are on their way to Berkeley and will be sent to our volunteers in the coming months. Without the support of user donations, these major new extensions to the SETI@home experiment would not have been possible.
Though the SETI@home experiment has been active for over eleven years, we are still branching out and exploring new scientific directions. As you may know, the data that your computer analyzes comes principally from the Arecibo radio telescope in Puerto Rico. While Arecibo is an immensely powerful telescope, it cannot see the entire sky. In the past we have been able to perform short duration observations at the Green Bank Telescope. But in the coming year, we plan to start continuous collection of data at the Green Bank Telescope, allowing us to listen in on parts of the sky that, up until now, we have not been able to reach.
We're also in the process of adding a new search algorithm to the SETI@home application. This new algorithm, called autocorrelation, will make us sensitive to certain broad band or spread spectrum transmissions without predicting their characteristics beforehand. No SETI sky survey has utilized this type of algorithm before.
SETI@home has always been a collaborative effort on a global scale; there is no other project on Earth that is quite as dependent on the help and contributions of the citizens of Earth at-large. In order to keep SETI@home up and running, as well as accomplish this year's scientific goals, we need your help. To make a secure tax-deductible donation click here, which will take you to a page of instructions on how to donate online or through mail via check. Any amount that you are willing to donate this holiday season would be a great help. Your contribution will not only allow SETI@home to continue to run as it has for the past eleven years, but also provide the opportunity to expand the search for intelligent life in exciting new directions. These efforts represent our best chance at answering the ever-elusive question: Are we alone?
Thank you for your support and continuing dedication to SETI@Home.
Sincerely,
Dr. Eric Korpela, Project Scientist
SETI@home 这是多么浪漫的项目哪,都出现在不少网络小说中的科学项目;
就这么没了,卡尔小说中那些私人赞助并没有如期出现...
强大如美国,也要放弃科学的终极追求了嘛...
好在科学家有无数方法可以渡过经济危机,点击捐助
目前只要几万刀,就可以低烈度的继续寻找外星人了...
动力源自::txt2tags
世界 Python 爱好者的顶级盛会 —— PyCon 大会将首次在中国举办!本次大会由(Python 软件基金会下的)PyCon.Org 正式授权 GTUG、TopGeek、CPyUG 等社区联合举办,由 Python 作者 Guido van Rossum 及其所在的 Google 公司提供关键支持。
本次大会云集了国内外最为杰出的 Python 开发者,国内 Python 领域的领军人物几乎悉数到场、作为 Python 重镇的各大知名公司几乎无一缺席,所有嘉宾都承诺将自己压箱底的 Python 绝活呈现给大家!沈游侠也会在大会上进行题为《Python,通向未来之路》的纯技术演讲,欢迎捧场。
12 月,让我们聚首上海!
大会订票已经开始 http://pycon.51qiangzuo.com 手速一定要快哦!
会议为期两天(12 月 3 日至 4 日),票价 60¥,全部用于会议期间的就餐费用,其余大会所有费用及礼品主要由 Google 等公司承担赞助。作为国内最大的赞助者,我们也同样很荣幸能够服务于广大中国 Python 爱好者。
ps:
N久前就开始义务的狂热的不断的推广 Python 了:
- 070322-introPy/
- 100820-introPy/
PyCon - PythonInfo Wiki 2003开始,每年举行一次,
后来,随着 Python 的发展,Google 的崛起,每年一次,已经无法满足全球 Python 程序猿的现摆了,
于是,每年,各国都有 PyCon 授权的正式大会来Party HIGH 一下,
Pycon Asia Pacific2010 在新加坡也整起来了,亲历了一下,很爽;
只是没想到,今年,中国说搞就搞起来了!
虽然 Python 在中国,社区不少,但是没有 JAVA/.NET/Ruby 等那么趁钱,光鲜,
大家都在自个儿happy 的用,不怎么出来吼;
但是,在中国的 Python 程序猿真的都很NB 哪,
不出来吼,整得是个NB社区样儿,学生们可不屑来学习的哪.
好了,夸了 的定期FB聚会,
大家一喝高了,就有了冲动,一冲动就成了...
其实我们玩的真不差了,相比国际上的玩法,有正式舞台了,多吼一吼,有益身心的!
反正,俺一定要出台了,大家也尽力来哈!
动力源自::txt2tags
¶ ScrapBook辅助工具之expidxlevels
嗯嗯嗯,从,,, 20041214101930 开始,坚持使用SCRAPBOOK :: Firefox Extension 进行离线网页的收集和整理了;
- 一直很爽,而且内置的导出功能,可以一键将本地收集的网页通过一个标准的框架页面,用树状索引进行发布;
- 使用 rsync 等等文件同步小工具,就可以发布一个静态的表述自个儿关注领域技术的纯资料网站了!
- 其实一直以来就发布有这类两个网站:
- 好处是那些优秀的文章,即使原文网站死了,依然在俺这儿原样可查,
- 问题是:
-
- 导出的那个索引树,随着时间的积累,已经大到无法忍受了!
- 比如说, floss.zoomquiet.org 的树,包含 2万多节点,自身体积已经超过5M
- 有网友吼,用 Chrome 都无法打开!
- 所以:
-
- 得想招精简如此多节点的索引树了,,,
- 为了时不时,在俺这儿打捞历史文章的亲们...
所以,有了 ZoomQuiet / scraptools — Bitbucket
其中的 expidxlevels.py 就是专门进行自动索引化简的...
以前在相关讲演中吼过,选择 SCRAPBOOK :: Firefox Extension的好点之一,就是有标准的XML 数据输出,好进行二次处理
- 设想:
-
- 将
scrapbook.rdf (自动生成的记录树关系的RDF)进行合理解析
- 整理成分级索引页面就可以解决单一索引的巨大加载问题了
- 杯具:
-
scrapbook.rdf 的设计很简洁:
- 根节点,索引各个
li
<RDF:Seq RDF:about="urn:scrapbook:root">
<RDF:li RDF:resource="urn:scrapbook:item20091114162455"/>
<RDF:li RDF:resource="urn:scrapbook:item20050206112141"/>
</RDF:Seq>
- 每个
li 也可能是一组 Seq
<RDF:Seq RDF:about="urn:scrapbook:item20070212000600">
<RDF:li RDF:resource="urn:scrapbook:item20070212000504"/>
<RDF:li RDF:resource="urn:scrapbook:item20070212000555"/>
</RDF:Seq>
- 不论
Seq 自身,还是真正的页面,都有一个描述节点来记录详情
<RDF:Description RDF:about="urn:scrapbook:item20051216104753"
NS2:id="20051216104753"
NS2:type=""
NS2:title="吉卜力的新作也用blog宣傳"
NS2:chars="UTF-8"
NS2:comment=""
NS2:icon=""
NS2:source="http://www.bigsound.org/portnoy/weblog/001318.html" />
- 如果只是分隔线,就是:
<NC:BookmarkSeparator RDF:about="urn:scrapbook:item20091113232313"
NS2:id="20091113232313"
NS2:type="separator"
NS2:title=""
NS2:chars=""
NS2:comment=""
NS2:icon=""
NS2:source="" />
那么一切就应该从 <RDF:Seq RDF:about="urn:scrapbook:root"> 节点开始爬就好的了,,,
- FT!:
-
- 不论内置的
xml.dom / xml.etree.ElementTree 还是伟大的 lxml
- 都不支持根据 XML 节点的属性进行搜索!
- 即使可以用 XPath 的算子过滤:
//NC[@RDF:about = "urn:scrapbook:root"] ,但是,没有库支持完全功能的XPath!
- 俺总不能用 XSLT 先写好过滤,然后再调用支持 XSLT 的浏览器获得中间结果給 Py 用吧?!
- 好的,有一堆 RDF 专用解析器
- 可是,对于俺,这么简单的需求,就是没有简单的处置方法嘛?!
- 解决:
-
- 冷静了一下,俺只是要进行简单的数据处理,并不一定要真的对 RDF 进行语义上的理解哪?!
- XML 自古就有一种原始的,条带化基于事件的处理模型,曰 SAX
- Py 内置有最简单的 expat库:
- 跟着样例快速完成了处理部分,速度也非常的快
def start_element(name, attrs):
if "RDF:Seq" == name:
CF.IS_SEQ = 1
CF.IS_DESC = 0
if "urn:scrapbook:root" == attrs['RDF:about']:
#print 'ROOT element:', name, attrs
CF.IS_ROOT = 1
CF.DICTRDF['ROOT']['id'] = attrs['RDF:about'].split(":")[-1]
CF.CRTID = attrs['RDF:about'].split(":")[-1]
CF.DICTRDF['ROOT']['li'] = []
else:
CF.IS_ROOT = 0
CF.CRTID = attrs['RDF:about'].split(":")[-1]
CF.DICTRDF['SEQ'][CF.CRTID] = []
else:
CF.IS_SEQ = 0
if "RDF:li" == name:
CF.IS_DESC = 0
CF.IS_LI = 1
if CF.IS_ROOT:
CF.DICTRDF['ROOT']['li'].append(attrs['RDF:resource'].split(":")[-1])
else:
CF.DICTRDF['SEQ'][CF.CRTID].append(attrs['RDF:resource'].split(":")[-1])
elif "RDF:Description" == name:
CF.IS_DESC = 1
CF.IS_LI = 0
CF.CRTID = attrs['RDF:about'].split(":")[-1]
CF.DICTRDF['DESC'][CF.CRTID] = {
'id':attrs['NS2:id']
,'type':attrs['NS2:type']
,'title':attrs['NS2:title']
,'source':attrs['NS2:source']
,'chars':attrs['NS2:chars']
,'icon':attrs['NS2:icon']
,'comment':attrs['NS2:comment']
}
- 技巧:
-
- 就是用一堆判定,将有限的情况进行区分
- 然后丢到个字典中,供给后续处理
{"ROOT":{'id':'','li':[]}
,"SEQ":{'item...':[]
,,,}
,"DESC":{'item...':{'id':''
,'type':"" # folder||separator
,'icon':''
,'title':''
,'source':''
,'chars':''
,'comment':''
}
,,,
}
}
好的,有了 RDF 正确的结构关系数据后,怎么优雅的输出成分层的索引页面?!
- 俺习惯用内置的文本模板功能,通过纯文本的嵌套完成 html 的输出
- 结果,发现,俺的网页整理到不同深度的目录中
- 要想进行递归式的树状生成,很容易引发递归过深,Py 崩溃的现象
// scrapbook/chrome/scrapbook.jar->content/scrapbook/output.js 中
processRescursively : function(aContRes)
{
this.depth++;
var id = ScrapBookData.getProperty(aContRes, "id") || "root";
this.content += '<ul id="folder-' + id + '">\n';
var resList = ScrapBookData.flattenResources(aContRes, 0, false);
for (var i = 1; i < resList.length; i++) {
this.content += '<li class="depth' + String(this.depth) + '">';
this.content += this.getHTMLBody(resList[i]);
if (ScrapBookData.isContainer(resList[i]))
this.processRescursively(resList[i]);
this.content += "</li>\n";
}
this.content += "</ul>\n";
this.depth--;
},
- SCRAPBOOK中的原生处理是硬递归的哪,,,
- Py 有优雅的迭代式,但是不那么容易用起来:
于是,一切安定团结了,,,
用 shell 包装个命令,想发布本地 SCRAPBOOK 仓库时,一键完成!
当然总是有不如意的,留存以后,或是有心人完善了:
- 美化平面索引页面
- 排版和颜色
- CSS 限宽效果用JS 进行动态扩展
- 自动对所有抓取的页面,嵌入原始链接的提示
- 对整体仓库生成 site map 帮助 google 收录 ...
- ~0.01h 起意,要折腾
- 0.5h rdf 理解
- 1h ElementTree 尝试
- 1h lxml 尝试
- ~2h RDF 解析模块收集
- ~1h rdflib 尝试
- ~0.5h 冷静
- ~0.5h expat完成解析
- ~1h 根索引页面输出
- ~2.5h 递归和迭代尝试
- ~2h 获得社区反馈,完成所有功能
合计,~13小时,哗,,,,大大超出原先半天的预计,纠其原因:
- 对XML体系的变态缺乏足够的敬畏
- 对递归的理解一直不扎实
事实证明:嘦不经过真实编程的理解,基本都是误解
动力源自::txt2tags
# -*- coding: utf-8 -*-
"""py - html Parser
- refactory py2pre.py from xhtml.py
Copyright (c) 2011 Zoom.Quiet
All rights reserved.
Redistribution and use in source and binary forms are permitted
provided that the above copyright notice and this paragraph are
duplicated in all such forms and that any documentation,
advertising materials, and other materials related to such
distribution and use acknowledge that the software was developed
by the zoomquiet.org. The name of the
University may not be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
"""
## Leo: tab_width=-4 page_width=80
__version__ = "v11.09.7"
__author__ = 'Zoom.Quiet '
__url__ = "http://blog.zoomquiet.org/pyblosxom/techic/PyBlosxom/plugins/py2pre.html"
__description__ = ".py export hmtl entry with syntaxhighlighter."
#from Pyblosxom import tools
def cb_entryparser(entryparsingdict):
"""
Register self as plain file handler
"""
entryparsingdict["py"] = parse
return entryparsingdict
def parse(filename, request):
#import os
entrydata = {}
source = open(filename, "r").read()
#print filenames
body = '%s ' % source
entrydata = {'body' : body
,'title' : filename.split("/")[-1]
}
return entrydata
嗯嗯嗯,一清点,居然这么长时间没有发布正式点的 blog 了哪!
- 想恢复每周的心得汇报,但是,俺有强迫症!
- PyBloxom 非常好玩,好用
- 但是,一直不甚完美
- 不过,这也是俺喜欢她的原因~
所有不完美之处,都可以自行 hacking!
- 所以:
-
- 安装了 Disqus
- 升級了 SyntaxHighlighter
- 修复了 py.py
- 修复了内置的分类索引
一直以来,PyBlosxom 没有内置一个靠谱的评注系统,是个囧事儿,
之前,俺为了节省流量,使用了 官方提供的 hosting...
- 结果伟大的功夫网,总是令展示效果不理想!
- 现在想通了,直接升级 到 SyntaxHighlighter 3.0.83
- 并指向本地的各种资源
- 比较贴心的是,这次有了 Dynamic Brush Loading
- 不用象以往,逐一JS 的加载了
function path(){
var args = arguments;
var result = [];
for(var i = 0; i < args.length; i++){
result.push(args[i].replace('@', '/pybshare/syntaxhighlighter_3.0.83/scripts/'));
};
return result;
};
SyntaxHighlighter.autoloader.apply(null, path(
'applescript @shBrushAppleScript.js',
'bash shell @shBrushBash.js',
'css @shBrushCss.js',
'diff patch pas @shBrushDiff.js',
'erl erlang @shBrushErlang.js',
'js jscript javascript @shBrushJScript.js',
'text plain @shBrushPlain.js',
'py python @shBrushPython.js',
'sass scss @shBrushSass.js',
'sql @shBrushSql.js',
'xml xhtml xslt html @shBrushXml.js'
));
SyntaxHighlighter.all()
- PS:
-
- 也有更加轻量的 prettify
- 但是,没有 SyntaxHighlighter 的高亮功能
- 而且,已经和 t2t 深度定制过,使用很好,也就不追赶了,,,
1.5 的插件体系好象有所变化,不那么简单的可以理解了,,,
py2pre.py
- 意图:
-
- 简单的将目录中的 .py 脚本渲染成合适的 html 展示
- 问题:
-
- 解决:
-
- 技巧:
-
- 直接复用 SyntaxHighlighter 的效能
- 将所有脚本内容丢到约定的
<pre> 中就好
#...
def parse(filename, request):
entrydata = {}
source = open(filename, "r").read()
body = '<pre class="brush: python">%s</pre>' % source
entrydata = {'body' : body
,'title' : filename.split("/")[-1]
}
return entrydata
这货不是 PyBlosxom 标准插件,只是发布辅助脚本
- 问题:
-
- 发现使用
pyblosxom-cmd staticrender --config </path/2/config.py> 生成的静态页面,分类索引页面有问题:
- 正常的分类索引中,只包含目录中一个文章
- 如果是 非内容目录,比如说py 脚本目录,倒是可以包含所有内容,可页面输出又有乱码
- 尝试:
-
- 吼了列表,没人理
- 也忘记以前是否正当了
- 追踪代码:
Traceback (most recent call last):
File "/usr/local/bin/pyblosxom-cmd", line 25, in <module>
sys.exit(command_line_handler("pyblosxom-cmd", sys.argv))
File "/usr/local/lib/python2.6/dist-packages/Pyblosxom/commandline.py", line 466, in command_line_handler
return f(command, argv)
File "/usr/local/lib/python2.6/dist-packages/Pyblosxom/commandline.py", line 362, in run_static_renderer
return p.run_static_renderer(options.incremental)
File "/usr/local/lib/python2.6/dist-packages/Pyblosxom/pyblosxom.py", line 409, in run_static_renderer
tools.render_url_statically(config, url, q)
File "/usr/local/lib/python2.6/dist-packages/Pyblosxom/tools.py", line 940, in render_url_statically
response = render_url(cdict, url, querystring)
File "/usr/local/lib/python2.6/dist-packages/Pyblosxom/tools.py", line 983, in render_url
p.run(static=True)
File "/usr/local/lib/python2.6/dist-packages/Pyblosxom/pyblosxom.py", line 182, in run
blosxom_handler(self._request)
File "/usr/local/lib/python2.6/dist-packages/Pyblosxom/pyblosxom.py", line 947, in blosxom_handler
renderer.render()
File "/usr/local/lib/python2.6/dist-packages/Pyblosxom/renderers/blosxom.py", line 330, in render
content = self.render_content(self._content)
File "/usr/local/lib/python2.6/dist-packages/Pyblosxom/renderers/blosxom.py", line 273, in render_content
self.render_template(parse_vars, "story", override=1))
File "/usr/local/lib/python2.6/dist-packages/Pyblosxom/renderers/blosxom.py", line 370, in render_template
{"entry": entry, "template": template})
File "/usr/local/lib/python2.6/dist-packages/Pyblosxom/renderers/blosxom.py", line 405, in _run_callback
defaultfunc=lambda x:x)
File "/usr/local/lib/python2.6/dist-packages/Pyblosxom/tools.py", line 780, in run_callback
output = func(input)
File "/home/zoomq/workspace/3hg/zoomquiet-default/pyblosoxm/zoomquiet/plugins/preformatter/markdown-plugin.py", line 44, in cb_story
...
日!这么深的调用栈?!
pyblosxom-cmd 命令工具
|
+-> commandline.py 解析参数,准备环境
|
+-> pyblosxom.py 调用工具
^ |
| +-> tools.py 组织插件,参数
| |
+----<-----+ 嗯嗯嗯?!回调 pyblosxom.py
使用 renderer.render() 和动态网站流程一样,输出内容
- FT! 具体分类目录在哪个环节生成基本找遍不到了,,,因为将所有需要渲染的,都丢到一个列表中了,,
- 解决:
-
- 其实复杂的技术问题,总是有很2的解决方案的
- 既然难以解决原有的渲染问题,那么 使用期待的页面替换有问题的就KO的哈!
- 俺的 category_static.py 插件生成的树状索引: category-index.html 很可以
- 那么对其进行相关裁剪,复制到对应目录中不就得了!?
先小小的增补一下category_static.py
# ...
for e in etree[p][1:]:
body += '<span id="%s" class="indents">%s</span><a href="%s%s.html">%s</a><br>\n'%(
"/".join(etree[p][0])
,"..."*len(etree[p][0])
,_baseurl
,e[1]
,e[0]
)
- 在前导空间的span 中增加代表文章所在分类目录的 id
那么 cp4idx2category.py 就可以简单的完成了:
# -*- coding: utf-8 -*-
'''
将 category_static.py 生成的树状分类索引页面,复制并修订为各个目录中的 index.html
'''
__version__ = 'cp4idx2category v11.09.7'
__author__ = 'Zoom.Quiet <zoomquiet+pyb at gmail dot com>'
import os
import sys
import re
import shutil
def cp4gen(path):
IDX = "%s/category-index.html"% path
p = re.compile("%s\/\d{4}"%path )
for root, dirs, files in os.walk(path):
if path == root:
pass
elif p.match(root):
pass
elif "plugin_info" in root:
pass
else:
aimpath = root.replace(path,"")
exp = ""
for i in open(IDX,'r'):
if '<span id="' in i:
if aimpath in i:
exp += i
else:
exp += i
open("%s/index.html"% root,"w").write(exp)
return
if __name__ == '__main__': # this way the module can be
if 2 != len(sys.argv):
print """ %s usage::
$ python cp4idx2category.py path/2/_static
| +- 生成的静态页面入口
+- 脚本自身
""" % VERSION
else:
path = sys.argv[1]
cp4gen(path)
只要每次,完成静态整站渲染后,用cp4idx2category.py刷一下,就KO!
不断维护的完美之折腾...
- PyBlosxom 静态化发布体系:
-
- Hg/Git 的 hooks 开发部署
- dot 的自动包含图片热区定义的 t2t 处理
- Leo 中的自动化发布脚本按钮
- ~0.01h 起意,要折腾
- ~.5h SyntaxHighlighter升級
- ~2.5h DISQUS 加装,主要是注册和文档查阅耗时
- ~1h 列表吼,E文真的很难以表述清楚...
- ~1h py2pre.py 重构完成
- ~1h cp4idx2category.py 山寨完成
- ~1.5h 记录成文
合计,7小时,哗,,,,
动力源自::txt2tags
"""
Summary
=======
This plugin is maintained at::
http://www.bluesock.org/~willg/pyblosxom/
Check that URL for new versions, better documentation, and submitting
bug reports and feature requests.
Usage
=====
This plugin goes through all the plugins you have installed on your blog
and extracts information about the plugin.
To kick it off, the url starts with ``/plugin_info`` .
If there are plugins you want to run that you don't want showing up,
list them in the ``plugininfo_hide`` property of your ``config.py`` file::
py["plugininfo_hide"] = ["myplugin", "myotherplugin"]
It takes a list of strings.
----
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Copyright 2002-2007 Will Guaraldi
SUBVERSION VERSION: $Id: plugininfo.py,v 2872b22e2ace 2011/10/27 07:08:25 zoomquiet+hg $
Revisions:
2007-07-07 - Converted documentation to restructured text.
2007-05-19 - Brushed up the code, fixed documentation, ...
2005-11-11 - Pulled into another new version control system
1.8 - (26 October, 2005) pulled into new version control system
1.7 - (09 December, 2004) fixed date_head issue and stopped showing
docstrings
1.6 - (04 May 2004) added comment handling
1.5 - (18 February 2004) added the ability to "hide" plugins so that
we don't talk about them
1.4 - (17 February 2004) added alphabetical sorting of plugins and fixed
num_entries issue
1.3 - (14 July 2003) added $plugincount variable
1.2 - (5/27/2003) minor fixes in the build_entry
"""
import Pyblosxom.plugin_utils
import Pyblosxom.entries.base
import time
import os.path
__author__ = "Will Guaraldi - willg at bluesock dot org"
__version__ = "$Date: 2011/10/27 07:08:25 $"
__url__ = "http://www.bluesock.org/~willg/pyblosxom/"
__description__ = "Shows information about plugins that you're running."
TRIGGER = "/plugin_info"
def verify_installation(request):
config = request.getConfiguration()
# check to see if the user has specified the "plugininfo_hide"
# property
if not config.has_key("plugininfo_hide"):
# the user doesn't have the property set, so we let them know
# they can set it and it prevents specified plugins from showing
# up.
print "Note: You can set 'plugininfo_hide' to hide plugins you " + \
"don't want showing up."
else:
# they do have plugininfo_hide set, so we verify that the value
# is valid-ish.
val = config["plugininfo_hide"]
if not type(val) in [ list, tuple ]:
print "'plugininfo_hide' must be a list of strings."
return 0
for mem in val:
if not type(mem) == str:
print "'plugininfo_hide' must be a list of strings."
return 0
return 1
def build_entry(request, mem):
"""build_entry(Request, plugin) -> PyBlosxom.entries.base.BaseEntry
Takes a plugin, extracts information from it, and builds a PyBlosxom
entry from the results. It returns the BaseEntry object.
"""
plugindata = []
plugindata.append("")
# previously we pulled __doc__, but more and more people are storing
# documentation for the plugin as well as license information--which
# isn't really what we want to show. we really want the author, version,
# and url for the plugin. currently these are stored in __author__,
# __version__, and __url__ (though those should be changed to something
# like VERSION, AUTHOR, and URL so as to avoid confusion with Python
# special things.
plugindata.append("AUTHOR: " + str(getattr(mem, "__author__", None)) + "\n")
plugindata.append("VERSION: " + str(getattr(mem, "__version__", None)) + "\n")
if hasattr(mem, "__url__"):
plugindata.append("URL: %s\n" % \
(str(mem.__url__), str(mem.__url__)))
plugindata.append(" ")
# build a dict of the metadata that generate_entry needs
d = { "title": mem.__name__,
"absolute_path": TRIGGER[1:],
"fn": mem.__name__,
"file_path": TRIGGER[1:] + "/" + mem.__name__ }
# build the body of the entry
body = "".join(plugindata)
entry = Pyblosxom.entries.base.generate_entry(request, d, body, None)
return entry
def cb_prepare(args):
request = args["request"]
data = request.getData()
config = request.getConfiguration()
antiplugins = config.get("plugininfo_hide", [])
plugins = Pyblosxom.plugin_utils.plugins
plugins = [m for m in plugins if m.__name__ not in antiplugins]
data["plugincount"] = len(plugins)
INIT_KEY = "plugininfo_initiated"
def cb_date_head(args):
"""
If we're showing plugins, then we don't want the date_head templates
kicking in--so we block that.
"""
request = args["request"]
data = request.getData()
if data.has_key(INIT_KEY):
args["template"] = ""
return args
def cb_staticrender_filelist(args):
"""
This is test code--trying to work additional bits into the static
renderer.
"""
request = args["request"]
filelist = args["filelist"]
flavours = args["flavours"]
config = request.getConfiguration()
antiplugins = config.get("plugininfo_hide", [])
plugins = Pyblosxom.plugin_utils.plugins
plugins = [m for m in plugins if m.__name__ not in antiplugins]
if plugins:
for mem in plugins:
url = os.path.normpath(TRIGGER + "/" + mem.__name__ + ".")
for f in flavours:
filelist.append( (url + f, "") )
for f in flavours:
filelist.append( (os.path.normpath(TRIGGER + "/index." + f), "") )
def cb_filelist(args):
request = args["request"]
pyhttp = request.getHttp()
data = request.getData()
config = request.getConfiguration()
if not pyhttp["PATH_INFO"].startswith(TRIGGER):
return
data[INIT_KEY] = 1
data['root_datadir'] = config['datadir']
config['num_entries'] = 9999
entry_list = []
antiplugins = config.get("plugininfo_hide", [])
plugins = Pyblosxom.plugin_utils.plugins
plugins = [m for m in plugins if m.__name__ not in antiplugins]
pathinfo = pyhttp["PATH_INFO"]
# if the browser requested the TRIGGER or TRiGGER/index, then we
# kick in and show plugin information for all plugins.
if pathinfo == TRIGGER or pathinfo.startswith(TRIGGER + "/index"):
plugins.sort(lambda x,y: cmp(x.__name__, y.__name__))
for mem in plugins:
entry_list.append(build_entry(request, mem))
return entry_list
# the browser requested to see information on a specific plugin.
# we need to pull off the flavour that was requested
# (if there was one). FIXME - this is a good candidate for a tools
# function.
pathinfo = pathinfo[len(TRIGGER):]
if pathinfo.startswith("/"): pathinfo = pathinfo[1:]
if pathinfo.endswith("/"): pathinfo = pathinfo[:-1]
filename, ext = os.path.splitext(pathinfo)
if ext[1:]:
data["flavour"] = ext[1:]
d = {}
for mem in plugins:
d[mem.__name__] = mem
# if the browser requested to look at a specific plugin, then
# we only show that one.
if d.has_key(filename):
return [build_entry(request, d[filename])]
# if the plugin the browser requested isn't in the list of
# plugins, then we return an empty list of entries--PyBlosxom
# will show a "that doesn't exist" message for that.
return []
# vim: tabstop=4 shiftwidth=4
# -*- coding: utf-8 -*-
# vim: tabstop=4 shiftwidth=4
"""
- zoomq 100419 fixed for export month/year with count
Walks through your blog root figuring out all the available monthly archives in
your blogs. It generates html with this information and stores it in the
$archivelinks variable which you can use in your head or foot templates.
You can format the output with the key "archive_template".
A config.py example:
py['archive_template'] = ' %(m)s/%(y)s'
Displays the archives as list items, with a month number slash year number, like 06/78.
The vars available with typical example values are:
b 'Jun'
m '6'
Y '1978'
y '78'
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Copyright 2004, 2005 Wari Wahab
"""
__author__ = "Wari Wahab - wari at wari dot per dot sg"
__version__ = "$Id: zqarchives.py,v 2872b22e2ace 2011/10/27 07:08:25 zoomquiet+hg $"
__url__ = "http://blog.zoomquiet.org/pyblosxom/techic/PyBlosxom/plugins/zqarchives.html"
from Pyblosxom import tools
import time, os
def verify_installation(request):
config = request.get_configuration()
if not config.has_key("archive_template"):
print "missing optional config property 'archive_template' which "
print "allows you to specify how the archive links are created. "
print "refer to pyarchive plugin documentation for more details."
return 1
class PyblArchives:
def __init__(self, request):
self._request = request
self._archives = None
def __str__(self):
if self._archives == None:
self.gen_linear_archive()
return self._archives
def gen_linear_archive(self):
config = self._request.get_configuration()
data = self._request.get_data()
root = config["datadir"]
archives = {}
archive_list = tools.walk(self._request, root)
fulldict = {}
fulldict.update(config)
fulldict.update(data)
template = config.get('archive_template',
' %(y)s.%(m)s')
# %(Y)s-%(b)s
#print fulldict["base_url"]
for mem in archive_list:
timetuple = tools.filestat(self._request, mem)
timedict = {}
for x in ["B", "b", "m", "Y", "y"]:
timedict[x] = time.strftime("%" + x, timetuple)
fulldict.update(timedict)
if not archives.has_key(timedict['Y'] + timedict['m']):
archives[timedict['Y'] + timedict['m']] = [template % fulldict,1]
else:
archives[timedict['Y'] + timedict['m']][1] += 1
archives[timedict['Y'] + timedict['m']][0] = template % fulldict
#print archives
#return
arc_keys = archives.keys()
arc_keys.sort()
arc_keys.reverse()
yearmonth = {}
result = []
#base archives walk and count every year's mounth
for key in arc_keys:
yearname = key[:-2]
if yearname in yearmonth.keys():
yearmonth[yearname][0] += archives[key][1]
yearmonth[yearname][1].append(archives[key])
else:
yearmonth[yearname] = [archives[key][1],[]]
yearmonth[yearname][1].append(archives[key])
#print yearmonth["2007"]
mon_keys = yearmonth.keys()
mon_keys.sort()
mon_keys.reverse()
#print mon_keys
for year in mon_keys:
#print "%s %s"%(year,yearmonth[year][0])
monode = yearmonth[year][1]
result.append("%s(%d)"%(fulldict["base_url"],year,year,yearmonth[year][0]))
if 1==len(monode):
#print "%s%s"%(monode[0][0],monode[0][1])
result.append("%s(%d)"%(monode[0][0],monode[0][1]))
else:
for m in monode:
#print m
#print "%s%s"%(m[0],m[1])
result.append("%s(%d)"%(m[0],m[1]))
#result.append("%s%s"%(month[0],month[1]))
#print result
self._archives = '\n'.join(result)
def cb_prepare(args):
request = args["request"]
data = request.get_data()
data["archivelinks"] = PyblArchives(request)
# -*- coding: utf-8 -*-
# vim: tabstop=4 shiftwidth=4
"""
- 仅仅要求可以根据指定顺序输出分类目录
Walks through your blog root figuring out all the categories you have
and how many entries are in each category. It generates html with
this information and stores it in the $categorylinks variable which
you can use in your head or foot templates.
You can format the output by setting "category_begin", "category_item",
"category_end" and properties.
Categories exist in a hierarchy. "category_start" starts the category listing
and is only used at the very beginning. The "category_begin" property begins a
new category group and the "category_end" property ends that category group.
The "category_item" property is the template for each category item. Then
after all the categories are printed, "category_finish" ends the category
listing.
For example, the following properties will use
to close a category and for each item:
py["category_start"] = ""
py["category_begin"] = ""
py["category_item"] = r'- %(category)s
'
py["category_end"] = " "
py["category_finish"] = ""
Another example, the following properties don't have a begin or an end but
instead use indentation for links and displays the number of entries in that
category:
py["category_start"] = ""
py["category_begin"] = ""
py["category_item"] = r'%(indent)s%(category)s (%(count)d) '
py["category_end"] = ""
py["category_finish"] = ""
There are no variables available in the category_begin or category_end
templates.
Available variables in the category_item template:
variable example datatype
======== ======= ========
base_url http://joe.com/blog/ string
fullcategory_urlencoded 'dev/pyblosxom/status/' string
fullcategory 'dev/pyblosxom/status/' string (urlencoded)
category 'status/' string
category_urlencoded 'status/' string (urlencoed)
flavour 'html' string
count 70 int
indent ' ' string
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Copyright 2004, 2005, 2006 Will Guaraldi
"""
__author__ = "Will Guaraldi - willg at bluesock dot org"
__version__ = "$Id: zqcategories.py,v 2872b22e2ace 2011/10/27 07:08:25 zoomquiet+hg $"
__url__ = "http://blog.zoomquiet.org/pyblosxom/techic/PyBlosxom/plugins/zqcategories.html"
#__url__ = "http://pyblosxom.sourceforge.net/"
__description__ = "Builds a list of categories."
from Pyblosxom import tools
import re, os
DEFAULT_START = r''
DEFAULT_BEGIN = r''
DEFAULT_ITEM = r'- %(category)s (%(count)d)
'
DEFAULT_END = " "
DEFAULT_FINISH = " "
DEFAULT_ROOT = []
def verify_installation(request):
config = request.getConfiguration()
if not config.has_key("category_template"):
print "missing optional config property 'category_template' which allows "
print "you to specify how the category hierarchy is rendered. see"
print "the documentation at the top of the pycategories plugin code "
print "file for more details."
return 1
class PyblCategories:
def __init__(self, request):
self._request = request
self._categories = None
config = self._request.getConfiguration()
self._baseurl = config.get("base_url", "")
self.genCategories()
def __str__(self):
if self._categories == None:
self.genCategories()
return self._categories
def genCategories(self):
config = self._request.getConfiguration()
root = config["datadir"]
start_t = config.get("category_start", DEFAULT_START)
begin_t = config.get("category_begin", DEFAULT_BEGIN)
item_t = config.get("category_item", DEFAULT_ITEM)
end_t = config.get("category_end", DEFAULT_END)
finish_t = config.get("category_finish", DEFAULT_FINISH)
#zoomq: configed order by mind the catrgorise
root_path_list = config.get("category_root_list", DEFAULT_ROOT)
cfgBaseUrl = config.get("base_url", "")
form = self._request.getForm()
flavour = (form.has_key('flav') and form['flav'].value or
config.get('default_flavour', 'html'))
#print flavour
# build the list of all entries in the datadir
output = ""
#@others
if 0==len(root_path_list): #as default walk and export Categories as word order
elist = tools.Walk(self._request, root)
output += self._subCategories(elist,root,"")
else:
for rootCategory in root_path_list:
subroot = "%s/%s"%(root,rootCategory)
self._baseurl = "%s/%s"%(cfgBaseUrl,rootCategory)
elist = tools.Walk(self._request, subroot)
output += self._subCategories(elist,subroot,rootCategory)
# then we join the list and that's the final string
#self._categories = "\n".join(output)
self._categories = output
def _subCategories(self,elist,root,rootname):
config = self._request.getConfiguration()
form = self._request.getForm()
flavour = (form.has_key('flav') and form['flav'].value or
config.get('default_flavour', 'html'))
start_t = config.get("category_start", DEFAULT_START)
begin_t = config.get("category_begin", DEFAULT_BEGIN)
item_t = config.get("category_item", DEFAULT_ITEM)
end_t = config.get("category_end", DEFAULT_END)
finish_t = config.get("category_finish", DEFAULT_FINISH)
# peel off the root dir from the list of entries
elist = [mem[len(root)+1:] for mem in elist]
# go through the list of entries and build a map that
# maintains a count of how many entries are in each
# category
elistmap = {}
for mem in elist:
mem = os.path.dirname(mem)
elistmap[mem] = 1 + elistmap.get(mem, 0)
self._elistmap = elistmap
#print self._elistmap
# go through the elistmap keys (which is the list of
# categories) and for each piece in the key (i.e. the key
# could be "dev/pyblosxom/releases" and the pieces would
# be "dev", "pyblosxom", and "releases") we build keys
# for the category list map (i.e. "dev", "dev/pyblosxom",
# "dev/pyblosxom/releases")
clistmap = {}
for mem in elistmap.keys():
mem = mem.split(os.sep)
for index in range(len(mem)+1):
p = os.sep.join(mem[0:index])
clistmap[p] = 0
# then we take the category list from the clistmap and
# sort it alphabetically
clist = clistmap.keys()
clist.sort()
output = []
indent = 0
output.append(start_t)
# then we generate each item in the list
for item in clist:
itemlist = item.split(os.sep)
num = 0
for key in self._elistmap.keys():
if item == '' or key == item or key.startswith(item + os.sep):
num = num + self._elistmap[key]
if not item:
tab = ""
else:
tab = len(itemlist) * " "
if indent > len(itemlist):
for i in range(indent - len(itemlist)):
output.append(end_t)
elif indent < len(itemlist):
for i in range(len(itemlist) - indent):
output.append(begin_t)
# now we build the dict with the values for substitution
d = { "base_url": self._baseurl,
"fullcategory": item + "/",
"category": itemlist[-1] + "/",
"flavour": flavour,
"count": num,
"indent": tab }
# this prevents a double / in the root category url
if item == "":
d["fullcategory"] = item
#print d
# this adds urlencoded versions
d["fullcategory_urlencoded"] = tools.urlencode_text(d["fullcategory"])
d["category_urlencoded"] = tools.urlencode_text(d["category"])
# and we toss it in the thing
output.append(item_t % d)
indent = len(itemlist)
output.append(end_t * indent)
output.append(finish_t)
# export define item's name
output[2] = output[2].replace(">/"," class='rootcategory'>%s/"%rootname)
return "\n".join(output)
def cb_prepare(args):
request = args["request"]
data = request.getData()
data["categorylinks"] = PyblCategories(request)
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
"""
If a filename contains a timestamp in the form of YYYY-MM-DD-hh-mm,
change the mtime to be the timestamp instead of the one kept by the
filesystem. For example, a valid filename would be
foo-2002-04-01-00-00.txt for April fools day on the year 2002.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Copyright 2004, 2005 Tim Roberts
"""
## Leo: tab_width=-4 page_width=80
# vim: tabstop=4 shiftwidth=4
import os, re, time
__author__ = 'Tim Roberts http://www.probo.com/timr/blog/'
__version__ = '$Id: pyfilenamemtime.py,v 2872b22e2ace 2011/10/27 07:08:25 zoomquiet+hg $'
DAYMATCH = re.compile('([0-9]{4})-([0-1][0-9])-([0-3][0-9])-([0-2][0-9])-([0-5][0-9]).[\w]+$')
def cb_filestat(args):
filename = args["filename"]
stattuple = args["mtime"]
mtime = 0
mtch = DAYMATCH.search(os.path.basename(filename))
if mtch:
try:
year = int(mtch.groups()[0])
mo = int(mtch.groups()[1])
day = int(mtch.groups()[2])
hr = int(mtch.groups()[3])
minute = int(mtch.groups()[4])
mtime = time.mktime((year,mo,day,hr,minute,0,0,0,-1))
except:
# TODO: Some sort of debugging code here?
pass
if mtime:
args["mtime"] = tuple(list(stattuple[:8]) + [mtime] + list(stattuple[9:]))
return args
|