小子 发布的文章

使用python Queue创建文档格式转换服务

该项目比较简单,只具备文件排队转换,不具有查看队列的功能,使用openoffice和unoconv作为转换程序,程序不提供建立系统服务

使用场景,一般使用thrift建立服务,将该程序挂载在里面。

#!/usr/bin/env python
#encoding: utf-8
#author: xiaozi <245565986@qq.com>
import Queue, threading, os, subprocess
import time

class Converter(object):
	def __init__(self):
		self._tasks = Queue.Queue()
		self._docs = Queue.Queue()
		self._pdfs = Queue.Queue()

		self._toPdfThreads = []
		self._toSwfThreads = []
		self._toPdfThreadNum = 4
		self._toSwfThreadNum = 4

		self._dispatchThread = threading.Thread(target=self.dispatchTask)
		self._dispatchThread.setDaemon(True)

		for i in range(self._toPdfThreadNum):
			pdfThread = threading.Thread(target=self.toPdf)
			pdfThread.setDaemon(True)
			self._toPdfThreads.append(pdfThread)

		for i in range(self._toSwfThreadNum):
			swfThread = threading.Thread(target=self.toSwf)
			swfThread.setDaemon(True)
			self._toSwfThreads.append(swfThread)
		
		self._dispatchThread.start()
		for i in range(self._toPdfThreadNum):
			self._toPdfThreads[i].start()
		for i in range(self._toSwfThreadNum):
			self._toSwfThreads[i].start()

	def addTask(self, path):
                print(path)
		self._tasks.put(path)

	def dispatchTask(self):
		while True:
			path = self._tasks.get()
			if not path:
				break
			ext = os.path.splitext(path)[1][1:].lower()
			if ext == 'doc' or ext == 'docx':
				self._docs.put(path)
			elif ext == 'pdf':
				self._pdfs.put(path)

	def toPdf(self):
		while True:
			path = self._docs.get()
			if not path:
				break
			subprocess.Popen(
				['python', 'unoconv', '-f', 'pdf', path]
			).communicate()
			newpath = os.path.splitext(path)[0] + '.pdf'
			self._pdfs.put(newpath)

	def toSwf(self):
		while True:
			path = self._pdfs.get()
			if not path:
				break
			# do something
			path_target = os.path.splitext(path)[0]
			if not os.path.exists(path_target):
				os.makedirs(path_target)
			subprocess.Popen(['pdf2swf', path, '-o', path_target + '/page%.swf', '-f', '-T', '9', '-t', '-s', 'storeallcharacters']).communicate()
			print(path)

	def close(self):
		self._tasks.put(None)
		self._dispatchThread.join()

		for i in range(self._toPdfThreadNum):
			self._docs.put(None)
		for i in range(self._toPdfThreadNum):
			self._toPdfThreads[i].join()
		# '转换为pdf'线程都结束之后,再结束'转换为swf'的线程
		for i in range(self._toSwfThreadNum):
			self._pdfs.put(None)
		for i in range(self._toSwfThreadNum):
			self._toSwfThreads[i].join()

if __name__ == '__main__':
	converter = Converter()
	converter.addTask('docs/paper.docx')

	time.sleep(3)

	converter.close()

下面是使用thrift建立的service

#!/usr/bin/env python
# encoding: utf-8

import sys, time

import convert

from thrift.transport import TSocket, TTransport
from thrift.protocol import TBinaryProtocol
from thrift.server import TServer

sys.path.append('thrift/gen-py')

from ConvertApi import *
from ConvertApi.ttypes import *

class ServerHandle(ConvertApi.Iface):
	def __init__(self):
		self.converter = convert.Converter()

	def addTask(self, path):
		self.converter.addTask(path)
		return True

def main(port = 1990):
	processor = ConvertApi.Processor(ServerHandle())
	transport = TSocket.TServerSocket(port = port)
	tfactory = TTransport.TBufferedTransportFactory()
	pfactory = TBinaryProtocol.TBinaryProtocolFactory()

	server = TServer.TThreadedServer(processor, transport, tfactory, pfactory)
	print('Starting server on :%d' % port)
	server.serve()

if __name__ == '__main__':
	port = 110110
	main(port)

thrift文件如下(该接口提供的比较简单):

service ConvertApi {
	bool addTask(1: string path);
}

discuzx模块数据调用

后台 > 门户 > 模块管理 > 数据调用

语法

DIY模块模板语法详解

[loop]...[/loop]标签来循环显示模块中的数据;如果要在模板中多处循环可以使用[loop1]...[/loop1]的方式扩展(只能扩展1-9个)

[order=N]...[/order]标签来替代默认loop中的第N个位置数据的展示,你可以使用[order1=N]...[/order1]的方式来扩展(对应[loop1],只能扩展1-9个)

其中:[order=odd]为奇数行,[order=even]为偶数行
[index=N]...[/index]的方式来指定第N条数据的展示方式,由该语法指定的数据不再在loop循环中出现,可以通过[index1]...[/index1]的方式来扩展(只能扩展1-9个)以实现同一条数据多处展示

实例

[index=1]

{title}

{summary} [详细]

[/index]

Openfire + err

安装Openfire

Openfire是IM的服务器端,下载地址:http://www.igniterealtime.org/downloads/index.jsp,选择linux rpm包下载

rpm -ivh openfire-3.7.1-1.i386.rpm

配置Openfire

首先在mysql里面建立一个数据库openfire,访问9090端口,进入网页进行初始化配置,增加一个用户,下面的err配置的时候会用到

安装err

err是一个IM机器人,项目地址:https://github.com/gbin/err,最简单的安装方法easy_install

easy_install err

建立几个需要用到的文件夹

mkdir -p /var/www/err
mkdir -p /var/log/err
mkdir -p /var/lib/err

chmod -R 777 /var/www/err
chmod -R 777 /var/log/err
chmod -R 777 /var/lib/err

cp /usr/lib/python2.6/site-packages/err-1.6.7-py2.6.egg/errbot/config-template.py /var/www/err/config.py

cd /var/www/err/

# 按照自己的要求修改吧,把上面配置的用户加到这个里面
vi config.py

其他

err.py命令启动的时候,可能还会报其他的模块不存在,直接easy_install好了

关于机器人插件的开发,可以参考项目主页上的wiki

YII修改CGridView默认排序

CSort里面是有一个defaultOrder的属性,但是GII自动生成的CURD不能这么来;后来找到个帖子(Model Search - Merging in a default start criteria),里面写的比较详细。但是他在Controller里面的条件判断需要改下。

$merge = new CDbCriteria;
if(empty($_GET['Depart_sort'])) {
	$merge->order = 'displayorder DESC';
}

主要的流程就是:

  1. 在控制器里面判断用户是否有指定排序字段,若没有则加上默认排序字段
  2. 传变量到模版里,由用户浏览页面,执行search
  3. model里面合并criteria的order条件

PolicyServer

最近做一个项目需要用到websocket,但是目前ie9及之前的版本都不支持,github上面出现了用flash模拟websocket的开源项目,由于使用flash时,flash是有权限限制的(这应该是为了安全性考虑),xmlsocket://会先访问843端口,获取授权文件。于是自己用python写了个,该程序依赖tornado库。http://inote.github.com/PolicyServer/

inotify-tools安装

前提

# 查看内核是否支持inotify机制
grep INOTIFY_USER /boot/config-$(uname -r)

下载安装

# https://github.com/rvoicilas/inotify-tools/
wget ...
tar zxf ...
# 若是没有configure文件,先运行
# ./autogen.sh
./configure --prefix=/usr/local/inotify
make && make install

基础的使用

inotifywait -rme modify,attrib,move,close_write,create,delete,delete_self /etc/asterisk

php杂记2

glob函数

// 获取所有php和txt文件,必须指定第二个参数为GLOB_BRACE才能使用{}
$files = glob('*.{php,txt}', GLOB_BRACE);

// 按照文件的修改时间排序
usort($files, create_function('$a,$b', 'return filemtime($a) - filemtime($b);'));

RecursiveDirectoryIterator

// 深度遍历文件夹
$path = realpath('/etc');

$objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::SELF_FIRST);
foreach($objects as $name => $object){
    echo $name . PHP_EOL;
}

PHP5.3下SQLSTATE[HY000] [2002] 非法的参数解决方案

将localhost改成127.0.0.1

来自:链接

SmartMarkdown插件bug

bug描述

SmartMarkdown是Sublime Text2的一个插件,该插件提供了smart table的功能,该功能在只有ascii字符的时候很完美,但当内容中出现中文,中文标点的时候就会产生错位,这是因为中文在屏幕上的显示占了两倍的英文所占有的位置。

bug修复

由于该插件里面计算的时候是按照字符的长度来计算的,所以他把中文字符串的长度也是计算为1的。我们要做的就是把非ascii字符串替换为两个占位字符,然后计算长度。找到插件目录下的table.py

# 21行添加
NONE_ASCII_PATTERN = re.compile(r'[^\x00-\xff]')
# 124行修改为
new_row.append(col + " " * (cols_length[i] - len(NONE_ASCII_PATTERN.sub('  ',col))))
# 163行修改为
col_len = len(NONE_ASCII_PATTERN.sub('  ', col))

修改过的文件:下载

CentOS一些配置及系统信息文件

大概三个月前,在公司做了个系统管理的程序(时间设置,网卡设置,系统时间设置,等等),现在记录下这些文件的路径,备忘。

# 网络默认参数
/etc/sysconfig/network

# dns
/etc/resolv.conf

# 单个网卡
/etc/sysconfig/network-scripts/ifcfg-eth*

# 登录系统时的提示信息
/etc/issue

# 登陆后的提示信息
/etc/motd

# 系统版本
/etc/redhat-release

# 时区
/etc/localtime
/usr/share/zoneinfo/*

/etc/sysconfig/clock

利用百度地图API拆分纯真IP数据库地址

导出txt,并转码

利用安装后的ip.exe导出txt文件,由于python处理gbk中文的时候会有些问题,所以我们直接选个编辑器打开之后,再以utf-8的编码保存就可以了。

提取所有地址

由于我们需要通过百度地图的API一个地址一个地址的查询,所以去除重复的地址可以减少我们的查询量。

# coding: utf-8
# python的去重方法很多,这里我们选用set类型
with open('ipdata.txt', 'r', encoding='utf-8') as handle:
    regex = re.compile(' +')
    addrs = set([])
    for line in handle:
        if line.strip() != '':
            address = regex.split(line.strip())[2]
            addrs.add(address)
    # addrs 就是我们过滤之后的所有地址了

上面我们已经过滤出所有的地址了,接下来就是使用百度的API来拆解这些地址。

import re, urllib, time, json
from urllib import parse, request

with open('ipdata.txt', 'r', encoding='utf-8') as handle:
    regex = re.compile(' +')
    addrs = set([])
    for line in handle:
        if line.strip() != '':
            address = regex.split(line.strip())[2]
            addrs.add(address)

    url = 'http://api.map.baidu.com/geocoder?output=json&key=你的API key'

    wh = open('address/list.txt', 'w', encoding="utf-8")
    for addr in addrs:
        addr_x = ''
        url_x = url + '&address=' + parse.quote(addr)
        # time.sleep(3)
        req = request.urlopen(url_x)
        res = req.read().decode()

        data = json.loads(res)

        if data['result']:
            location = str(data['result']['location']['lat']) + ', ' + str(data['result']['location']['lng'])
            url_x = url + '&location=' + parse.quote(location)

            req = request.urlopen(url_x)
            res = req.read().decode()

            data = json.loads(res)
            if data['result']:
                component = data['result']['addressComponent']
                addr_x = component['province'] + ' ' + component['city'] + ' ' + component['district']
        wh.write(addr + '\t' + addr_x + '\n')
    wh.close()

未完待续...

wxpython练手

学习wxpython的练手软件,写的比较简单,名为扫描,实际上每次只能扫描一个端口。

PS:py2exe打包的程序伤不起啊,小小的一个程序,依赖库什么放进去竟有20多M。
setup里面设置的icon_resources居然没成功,不知道是不是哪儿写错了。

源码:下载

使用Stylus来写css

less sass stylus都可以用来简化css的书写,我选用stylus主要有以下几点原因:

  • Stylus is "pythonic"
  • stylus的语法看上去更像一门编程语言

其中有一点蛋疼的是,哥没找到怎么设置输出css的缩进字符,个人还是比较喜欢用tab缩进。安装完nodejs后安装stylus和nib

npm install stylus nib

Windows7的都安装到C:\Users\用户名\node_modules\stylus目录下了,使用nib的时候,要

stylus --include C:\\Users\\用户名\\node_modules\\stylus test.styl

nib库中定义好了很多常用的css,可以直接调用,方便很多。

python http server

前端调试的时候,有的时候需要网络环境,哥对bat不懂,自己就瞎凑了个代码出来,以下代码需要python3.x的支持。

@echo off
set port=8080
if not ""%1"" equ """" set port=%1
python -m http.server %port% | explorer http://localhost:%port%
@echo on