##language:zh #pragma section-numbers on '''项目标准化 之 “自学问卷”生产工具 EasyPaper''' ''邮件技术部·项目管理组· ZoomQuiet '' 2005-06-09 <> = 概述 = '''EasyPaper''' 是在原先 内网自学问卷 系统基础上的加强 原先的自学问卷通过PHP页面组织题目,MySQL 记录答题情况,是非常实用的,但是没有问卷管理的功能,所有问卷是通过手工编辑页面完成的…… 那未,我们马上跟随各种项目标准化活动中要大量的使用问卷来进行培训的话,什么样的问卷工具可以满足最基础的需求呢? 需求规约 1. 问卷格式 * 顺序答卷 * 只有对才可以通过 2. 方便的试题发布: * 简单的模板系统 可以快捷修改所有页面元素 * 简明扼要的问卷内容记述模式,可以自由修改 * 可以随时重新发布原先发布过的问卷 3. 方便的成绩统计: * 按照问卷整理;谁,何时完成,成绩? = 设计 = 简单分析原有的问卷系统后,重新设计DB,和分折页面为模板后,自然的使用 Python 快捷组织代码,实现所有功能 其实只有一个功能:'''理解问卷的内容,然后自动生成一批问卷页面,发布出去!''' * 其中,''理解问卷的内容'' 就是要有一种方便的文本方式来设计问卷 * ''自动生成'' 就是要自动读入模板,将理解的问卷题目自动填写到适当的位置,然后写到对应的目录中 * ''发布!'' 最简单,由Apache 完成,我们不用考虑什么 首先利用Python 的灵活数据结构设计了问卷的记述: {{{#!python paper={ "pname":"问卷名称,最好写明所属规范集合,比如说:项目管理标准化 之 “项目计划与控制流程” ", "desc":"问卷描述,版本号什么的", "complete":5, #多少道题 "done":2, # 问卷状态 0:准备中|1:进行中|2:已发布 "ask":( ["1. “计划与控制流程”并不关注的项目活动是:", [ ("a","备选答案1"), ("b","备选答案2"), ... ], "正确答案"], ... ) } }}} 然后对应的,约定了config 文本格式的简化问卷定义格式: {{{ # 以设置文件格式来声明问卷 # 注意所有标点符号,要使用中文的双字节字符! [description] pname = 问卷名称,最好写明所属规范集合,比如说:项目管理标准化 之 “项目计划与控制流程” desc = 问卷描述,版本号什么的 complete= 多少道题 done = 问卷状态 0:准备中|1:进行中|2:已发布 [ask1]# 题目栏 ask+顺序号 number = 4 问题有多少选项 question= 题干 a = 备选答案1 b = 备选答案2 ... key = a 正确答案 ... }}} 模板的处理,就利用Python 的文本替换功能,即最基础的打印格式化字串替换功能,来完成,无它…… = 使用 = 使用是非常简便的,将设计好的问卷文件安放到专用目录,然后运行 "questionnaire.sh" 专门的组合工具调用脚本一切都自动执行了 == 安装 == 环境 需要的环境支持有: 1. Apache 2.0 以上,支持PHP 2. PHP 4.0 以上,支持MySQL * 有ADOdb V4.62 支持 3. MySQL 4.1 以上 4. Python 2.4 以上 * 安装有 py24-sqlobject-0.6 以上支持 MySQL 操作 * ADOdb for Python 1.13 以上支持 安装 从CVS中检出代码 cvs.internal.sina/mailtech PM/scm/questionnaire/* 复制到适当的目录中: {{{ ├──adodb-py113 ADOdb for Python │ └──adodb │ ├ ... │ └ setup.py 直接#python setup.py install ├──pm050531 原版问卷,纪念,不作使用 ├ cfg2paper.py .cfg 问卷设计文本解析脚本 ├ commfunction.php 问卷回答响应脚本 ├ conn.php 数据库连接PHP脚本 ├ index.php 问卷总列表展示 ├ questionnaire.leo 工程组织文件,使用Leo 管理所有代码的开发 ├ questionnaire.py 问卷生产脚本 ├ questionnaire.sh 综合调用shell ├──_cfg 问卷设计文本安装目录 ├──_papers 问卷信息数据发布目录 │ └ __init__.py 自动模板文件加载脚本 └──_template 模板文件目录 ├ ask-form.php ├ ... ├ paper-ask.py ├ ... └ questionnaire.sql DB库表设计文件 }}} 初始化DB 使用 template/questionnaire.sql 来创建DB的表,库名建议使用 "questionnaire" 否则需要对 conn.php;questionnaire.py 进行修正 == 管理 == 发布 将整个工具目录发布出去吧! 比如说所有文件所在目录 easypaper 通过Apache 发布为 {{{`http://u'site/paper/`}}} 则 默许的 index.php 会组织展示所有在使用的问卷链接 问卷生产 1. 将设计好的问卷 xxx.cfg 上传到 '''_cfg''' 目录中 2. 运行 '''questionnaire.sh''' 脚本 3. 将自动生成问卷到 xxx 目录,并组织到 index.php 中展示 问卷管理 1. 问卷答过,投入历史,不作当前培训主题? * 修改问卷设计文本中 '''done''' 的值为 ''2'' * 重新运行 '''questionnaire.sh''' 脚本 * 一切完成 2. 要彻底清除问卷? * 假定对应的问卷设计文本是 '''a.cfg''' * 删除 ''_cfg'' 目录中的 '''a.cfg''' * 删除 ''_paper'' 目录中的 '''a.py''' * 重新运行 '''questionnaire.sh''' 脚本 * 一切完成 = 技术笔记 = 虽然整个思路简要,明确,又有 Python 这一得力语言,但是还是要使用一些高级技巧才可以完成任务 == 模块自加载 == * 原来设定,''_paper'' 目录中存放以标准Python 数据结构组织的文本,可以直接加载为属性对象,而且是动态加载的 * 但是发现 使用 ``from _paper import * `` 不能载入已有的文件为对象! * 原来不是一个空的{{{`__init__.py`}}} 就可以令Python 动态识别模块文件滴! * 参考其它产品的代码,照猫画虎 完成以下 {{{`__init__.py`}}}: {{{#!python import sys,os,string import fnmatch # 自动声明所有模块 # Define what gets imported with a 'from IPython import *' __all__ = [] for f in os.listdir('_papers'): if fnmatch.fnmatch(f, '*.py'): if ("__init__" in f): pass else: __all__.append(string.split(f,".")[0]) print __all__ glob,loc = globals(),locals() for name in __all__: __import__(name,glob,loc,[]) }}} * 安装到 ''_paper'' 目录中,就可以自动加载所有此目录中的文件为模块了! == 动态模块引用 == 嗯嗯,解决了自动加载,引用时又有问题,毕竟目录搜索出来的是文件名,文本!已加载的模块那是对象! 最后是简单的 '''eval()''' 解决问题! 想来 '''eval()''' 作的就是尝试将其中的字串在系统环境中运行一把,返回当前匹配对象 === __import__ === * 是不是可以使用`__import__`来导入,不过导入前需要考虑将文件名的路径加到sys.path中,用完再释放掉。 -- limodou * 实际上使用 `__init__.py` 后,已经在名称空间了,只是因为仅仅是简单的数据结构,直接使用就好的,否则,还是正常的按照对象实例化一下子就好的说……Karrigell 开发中就发现此问题了 -- ZoomQuiet <> == 简要模板 == * 高级 字串格式化替换 * locals() 将当前运行空间中的所有对象以字典方式转换出来 可以使用 %(varName)s 的模式进行字串替换 * 这样一来就形成了一个天然的模板系统 * 比如说模板文件 ask-item.php {{{
%(ppqitem)s }}} * 使用时,就是读入,然后替换文件,组织,输出为其它文件 * 比如说 {{{#!python ff = open("paper.php","w") exp = "" template = open(ask-item.php,"r").read() for item in question: ppqvar = item[1][0] ppqitem = item[1][1] exp +=template%locals() ff.write(exp) }}} * 其中 '''question''' 是一个结构化数据对象 * 在循环中,'''locals()''' 会自动将当前环境对象中与模板字串中替换字串声明匹配的对象值替换入! ---- 动力源自:'''[[http://txt2tags.sf.net|txt2tags]]''' ## moin code generated by txt2tags 2.2 (http://txt2tags.sf.net) ## cmdline: txt2tags -t moin readme.t2t ---- -- ZoomQuiet (<>)