Snake's Home

snake


  • 首頁

  • 歸檔

  • 標籤
Snake's Home

next打开空白的问题

發表於 2017-01-04

最近打不开网站了,经过查找:
关于 hexo 主题 nexT 博客加载空白问题

GitHub Pages 禁止了 source/vendors 目录的访问.

我的解决方案:
去next主题下:

1
git pull

总算能访问了。
悲剧了,其他的我都要重新设置,页面已经显示很丑了。

有时间再整整吧。

Snake's Home

licenseforsourcetree

發表於 2016-11-16 | 分類於 工具

安装了sourcetree, 竟然过期了。过了很久都不知道怎么弄。
恰巧新的电脑上,也要安装。
误打误撞,解决了这个问题。

在安装的时候,选择已有账号: adogandamouse(我以前已经注册过的)
注册
然后它会发个邮件。
邮件
verify 一下。
打开页面
下载
就可以看到免费的license 了,下载下来。
新安装的直接就可以继续。

mac上直接拖进去。

 

Snake's Home

Jmetertestmobileapi

發表於 2016-11-14 | 分類於 工具

The Apache JMeter™ application is open source software, a 100% pure Java application designed to load test functional behavior and measure performance.

What does it do?

Apache JMeter features include:
Ability to load and performance test many different server/protocol types:

Web - HTTP, HTTPS
SOAP / REST
FTP
Database via JDBC
LDAP
Message-oriented middleware (MOM) via JMS
Mail - SMTP(S), POP3(S) and IMAP(S)
Native commands or shell scripts
TCP
Complete portability and 100% Java purity.

How to build a test plan?

Add “Test Plan” –>”Threads”–>”Thread Group”.

Add “Thread”–>”Sampler”–>”HTTP Request”.
Then configure the request.
image

Add “User Defined Variables” and define global variables.

Add “HTTP Header Manager” to define Json type:
Content-Type : application/json; charset=utf-8

How to get Token and sessionId:

Add “Post Processors”–>”Regular Expression Extractor”
configure like this:
image

How to add stress

configure like this:
stress = threads * loop count
image

Check Result: Under listener
image
image
image

Run as command:
jmeter -n -t test.jmx -l test.jtl

So can run in Jenkins.
Run Jmeter on Jenkins

Snake's Home

PIL

發表於 2016-10-23 | 分類於 python

首先得温习一些命令:

执行系统命令

主流的有4种os.system()、os.popen()、commands.getstatusoutput()、subprocess.Popen()

1
2
3
4
5
6
7
8
9
10
11
os.system() 这种是最简单的,但是只能执行,不能获取返回值
os.popen() 这种能拿到返回值,但是不能拿到返回状态
commands.getstatusoutput() 这种在Windows上会出现兼容性问题,无法执行(亲测),但是在Linux平台上还是挺好用的
subprocess.Popen() 目前我采用的是这种方式,给一个这种方式的demo
import subprocess # 导入subprocess模块
cmd = 'ls -al' # 定义需要执行的cmd
child = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) # 执行系统命令
# child.wait() # 等待系统命令返回后再执行后续命令,看需要可加可不加
(stdoutdata, stderrdata) = child.communicate() # 获取返回数据
print child.returncode # 打印查看命令的returncode
print("stderrdata:%s") % stderrdata # 打印查看

log记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# util.py
#! /usr/bin/env python
# -*- coding:utf-8 -*-

import logging # 导入logging模块

# 创建一个handler,用于写入日志文件
fh = logging.FileHandler('./test.log')

# 再创建一个handler,用于输出到控制台
ch = logging.StreamHandler()

# 定义handler的输出格式formatter
formatter = logging.Formatter('[%(asctime)s][%(filename)s][%(levelname)s]:%(message)s')
fh.setFormatter(formatter)
ch.setFormatter(formatter)

# 创建一个logger
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
logger.addHandler(fh)
logger.addHandler(ch)

# test.py
import util

util.logger.debug("i am debug")

# console输出
[2016-10-17 11:19:36,999][monkey.py][DEBUG]:i am debug

使用python random模块的choice方法随机选择某个元素

1
foo = ['a', 'b', 'c', 'd', 'e']
from random import choice
print choice(foo)

使用python random模块的sample函数从列表中随机选择一组元素

1
list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]  
slice = random.sample(list, 5)  #从list中随机获取5个元素,作为一个片断返回  
print slice  
print list #原有序列并没有改变。

每次安装PIL出错, 最近找到一篇文章
Mac安装PIL模块

1
sudo easy_install -f http://www.pythonware.com/products/pil/ Imaging

Windows安装PIL

Snake's Home

libimobiledevice

發表於 2016-10-16 | 分類於 ios_test

Android 及 iOS 常用操作命令

最近做 iOS 相关的事情比较多,发现其实 iOS 下还是有很多和 android 可以对应的操作,所以在此记录一下。

备注: iOS 用到的ideviceinstaller,idevicesyslog 等命令安装命令为:brew install libimobiledevice

应用相关

1、 安装应用(真机)

1
2
3
4
5
6
Android

adb install xxx.apk
iOS

ideviceinstaller -i xxx.ipa

2、 卸载应用(真机)

1
2
3
4
5
6
Android

adb uninstall <packageName>
iOS

ideviceinstaller -U <bundleId>

3、 获取应用唯一标识

其实也不算唯一标识了,只是大部分操作会用到。

Android: packageName
获取方式很多,只举其中一个比较容易的:

1
2
3
4
5
6
7
8
9
10
$ apktool d xxx.apk -o xxx
$ cd xxx
$ cat AndroidManifest.xml | grep -o "package=\".*\"" | sed "s/package=\"//g" | sed "s/\"//g"
com.test
iOS:bundleId

$ unzip xxx.ipa
$ cd Payload/xxx.app
$ defaults read `pwd`/Info CFBundleIdentifier
com.test

4、从源码构建应用安装包

这里只举 debug 包

android(现在 android studio 项目已经很流行了,所以就不说 ant 了)

1
$ cd /source-folder/

注意:没翻墙情况下貌似不能自动下载 gradle ,那么可以自行下载 gradle 后用 gradle 的 bin 文件代替此处的 gradlew 。

1
$ ./gradlew build

build 完的 apk 包放在 Application/build/outputs/apk 中
iOS 真机

1
2
3
4
$ cd /source-folder/
$ PROJECT=<your-project-name>
$ xcodebuild clean -project $PROJECT.xcodeproj -configuration Debug -alltargets
$ xcodebuild archive -project $PROJECT.xcodeproj -scheme $PROJECT -archivePath $PROJECT.xcarchive

注意,末尾的 exportProvisioningProfile 参数值是在 Xcode 的 Performance->Accounts->Apple ID->View Details 窗口的下半部分看到的名称。如 iOS Team Provisioning Profile: chj.ToDoList

1
2
3
4
$ xcodebuild -exportArchive -archivePath 

$PROJECT.xcarchive -exportPath $PROJECT -exportFormat ipa -exportProvisioningProfile "your provision profile"
# build 完的 ipa 包直接就放在当前目录

设备相关

1、查看设备中的应用列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Android

$ adb shell pm list packages
package:com.miui.gallery
package:com.xiaomi.tv.gallerylockscreen
...
如果有 drozer 的话能显示得更清晰

dz> run app.package.list
com.miui.gallery (图库)
com.xiaomi.tv.gallerylockscreen (锁屏画报)
...
iOS

$ ideviceinstaller [-u <device-udid>] -l
Total: 46 apps
com.xiaojukeji.didi - 滴滴出行 4.1.5.0
com.tencent.mqq - QQ 6.0.0.424
...

2、获取真机实时日志

1
2
3
4
5
6
Android

adb [-s <device-name>] logcat
iOS

idevicesyslog [-u <device-udid>]

3、获取当前连接的设备列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Android

$ adb devices
iOS

注意:这里列出的设备包括模拟器及 mac 电脑本身
$ instruments -s devices
补充:
mac安装以下内容:

brew install usbmuxd
brew install ideviceinstaller
可以获取ios手机的udid

idevice_id -l
对于脚本中要使用默认设备的场景很方便。

ideviceinfo 获取ios手机信息
ideviceinfo -x 获取ios手机信息,并以xml形式显示

ideviceinfo -k ProductType 获取手机型号
ideviceinfo -k ProductVersion 获取系统版本
ideviceinfo -k DeviceName 获取手机名称

Android

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import os
import re
import shutil
import subprocess
import time



englishtown_url = "http://10.128.42.155:8080/view/Engage/job/engage-android-release/{0}/artifact/engage/build/outputs/apk/engage-englishtown-live-release.apk"
corporate_url = "http://10.128.42.155:8080/view/Engage/job/engage-android-release/{0}/artifact/engage/build/outputs/apk/engage-corporate-live-release.apk"

current_dir = os.path.split(os.path.realpath(__file__))[0]
report_path = current_dir + "/apk/"
old_path = current_dir + "/old/"
new_path = current_dir + "/new/"


def exec_command(cmd):
result = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
(stdoutdata, stderrdata) = result.communicate()
if (re.search("error", str(stdoutdata))):
print "error occur!"
else:
return stdoutdata

def check_folder(name):
if os.path.exists(name):
# os.removedirs(report_path)
shutil.rmtree(name)

else:
pass
os.makedirs(name)


def get_url(url,folder_name):
cmd = 'curl -O {0}'.format(url)
os.chdir(folder_name)
exec_command(cmd)
os.chdir(current_dir)


def check_md5(file):
cmd = 'md5 {0}'.format(file)
md5_id = exec_command(cmd)
print "{0} md5 value is {1}".format(file, md5_id)


def find_devices():
rst = os.popen('adb devices').read()
devices = re.findall(r'(.*?)\s+device',rst)
if len(devices) > 1:
Ids = devices[1:]
else:
Ids = []
return Ids

def find_apks():
apks = []
for c in os.listdir(os.getcwd()):
if os.path.isfile(c) and c.endswith('.apk'):
apks.append(c)
return apks

def upgrade_englishtown(old_folder,new_folder):
old_version = raw_input("please input the old build number")
new_version = raw_input("please input the new build number")
old_apk_name = englishtown_url.format(old_version).split("/")[-1]
get_url(englishtown_url.format(old_version),old_folder)
new_apk_name = englishtown_url.format(new_version).split("/")[-1]
get_url(englishtown_url.format(new_version),new_folder)
return old_apk_name, new_apk_name

def upgrade_corporate(old_folder,new_folder):
old_version = raw_input("please input the old build number")
new_version = raw_input("please input the new build number")
old_apk_name = corporate_url.format(old_version).split("/")[-1]
get_url(corporate_url.format(old_version),old_folder)
new_apk_name = corporate_url.format(new_version).split("/")[-1]
get_url(corporate_url.format(new_version),new_folder)
return old_apk_name, new_apk_name

def install_apks(apk_name):
cmd = 'adb install {0}'.format(apk_name)
print cmd
result = exec_command(cmd)
print result
if (re.search("success", result)):
print "install success!"
else:
print "install fail!"



def main():

choose = raw_input("please select the type you want: 1:md5,2:upgrade_englishtown,3:upgrade_corporate")
if choose == '1':
build_id = raw_input("please input the build number")
check_folder(report_path)
get_url(englishtown_url.format(build_id),report_path)
get_url(corporate_url.format(build_id),report_path)
if os.path.exists(report_path):
for file in os.listdir(report_path):
check_md5(report_path + file)
else:
"please check your network!"
elif choose == '2':
check_folder(old_path)
check_folder(new_path)
(old_apk,new_apk) =upgrade_englishtown(old_path,new_path)
print "old apk is : %s" % old_apk
install_apks(old_path + old_apk)
print "new apk is : %s" % new_apk
install_apks(new_path+new_apk)


elif choose == '3':
check_folder(old_path)
check_folder(new_path)
(old_apk, new_apk) = upgrade_corporate(old_path,new_path)
print "old apk is : %s" % old_apk
install_apks(old_path + old_apk)
print "new apk is : %s" % new_apk
install_apks(new_path+new_apk)

else:
print "please select again"




if __name__ == '__main__':
main()

IOS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import os
import re
import subprocess
import time

current_time = time.strftime('%Y-%m-%d-%H-%M-%S', time.localtime(time.time()))


class iosinformation():
def exec_command(self, cmd):
result = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
(stdoutdata, stderrdata) = result.communicate()
# print("Result is:%s") % stdoutdata
if (re.search("No device found", str(stdoutdata)) or re.search("Could not connect", str(stdoutdata))):
print "Please connet it agian, or add permission like: brew install libimobiledevice --HEAD,sudo chmod -R 777 /var/db/lockdown/"
else:
return stdoutdata

def Get_UUID(self):
cmd = 'idevice_id -l'
uuid = self.exec_command(cmd)
return uuid

def Get_Device_Name(self):
cmd = 'ideviceinfo -k DeviceName'
device_name = self.exec_command(cmd)
return device_name

def Get_Device_information(self):
cmd = 'ideviceinfo -k ProductVersion'
device_information = self.exec_command(cmd)
return device_information

def Get_Device_Product_type(self):
cmd = 'ideviceinfo -k ProductType'
product_type = self.exec_command(cmd)
return product_type

def List_All_Pakages(self, uuid):
cmd = 'ideviceinstaller -u {0}'.format(uuid)

print cmd
all_pakages = self.exec_command(cmd)

return all_pakages

def List_All_Logs(self, uuid):
all_logs = "idevicesyslog -u {0}".format(uuid)
return all_logs

def Take_Screenshot(self):
current_dir = os.path.split(os.path.realpath(__file__))[0]
cmd1 = "idevicescreenshot {0} + '/' + screenshot-DATE.tiff".format(current_dir)
cmd2 = "sips - s format png {0}.tiff - -out {1}.png".format(current_time, current_time)
self.exec_command(cmd1)
print "ok"
# self.exec_command(cmd2)

def Install_Ipa(self, ipa):
cmd = 'ideviceinstaller -i {0}'.format(ipa)
result = self.exec_command(cmd)
return result

def Uninstall_Ipa(self, appid):
cmd1 = 'ideviceinstaller -l'
cmd2 = 'ideviceinstaller -U {0}'.format(appid)
result = self.exec_command(cmd1)
appids=[]
for id in result.split('\n'):
if re.search('-',id):
str = id[0:id.find("-")].strip()
appids.append(str)

else:
pass
print appids
if appid in appids:
result = self.exec_command(cmd2)
else:
print "The appid dosen't exit in the devices"



# cmd2 = 'ideviceinstaller -u appid'.format(appid)
# result = self.exec_command(cmd)
# return result


def main():
ios = iosinformation()

uuid = ios.Get_UUID()
print " uuid is {0}".format(uuid)
device = ios.Get_Device_Name()
print " device is {0}".format(device)
device_info = ios.Get_Device_information()
print " device_info is {0}".format(device_info)
product_type = ios.Get_Device_Product_type()
print " product_type is {0}".format(product_type)
# all_pakagas = ios.List_All_Pakages(uuid)
# print " all_pakagas is {0}".format(all_pakagas)
ios.Take_Screenshot()
ios.Install_Ipa('/Users/anderson/testcode/python/flask/uploads/englishtown_corporate_daily-190-2016.07.15.ipa')
#ios.Uninstall_Ipa("com.ef.engage.englishtown.uat.dailydebug")



if __name__ == '__main__':
main()
Snake's Home

实现无AppStore分发iOS应用

發表於 2016-10-15 | 分類於 ios_test

为啥写这篇文章?因为前面一个事故,送AppStore的版本发错了。一打听,这样的事故发生过好几次。

苹果有太多限制,在测试中拿到包不是易事。
目前流行的有如下几种:

1.使用iTunes将iPa同步到手机中;

2.使用itms-services协议进行下载分发;

3.使用第三方工具进行下载分发,如:蒲公英,fir…

现在就以2来说说如何自己来实现分发。

流程如下:

用Xcode打包IPA版本
搭建本地Web服务器
开启HTTPS
编写好对应的.plist文件
上传ipa、.plist、ca证书到Web服务器,配置好index.html
在手机上用Safari打开链接,完成下载

首先是要搞定证书。

我们得解决证书的问题。可以参考iOS 证书申请和使用详解
如果搞不定,可以找开发帮忙。
把生成的证书放到server上。

用flask 搭建web service, 比较简单。
将证书放到upload中,添上如下代码,可以上传下载证书了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@app.route('/upload', methods=['POST'])
def upload():
uploaded_files = request.files.getlist("file[]")
filenames = []
for file in uploaded_files:
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'],filename))
filenames.append(filename)
return render_template('upload.html', filenames=filenames)

@app.route('/down_loads')
def down_loads():
if request.method=="GET":
if os.listdir(os.path.join('uploads')):
files = os.listdir(os.path.join('uploads'))
return render_template('down_loads.html',files=files)
abort(404)

编写plist文件

原理是通过Safari解析链接中的”itms-services://“来实现的。
链接指向plist,plist指向IPA。
例如:

1
<a title="iPhone" href="itms-services://?action=download-manifest&url=https://192.168.**.***/install.plist">Download</a>

Safari会去读取install.plist中的信息,如:iOS应用的名称、版本、安装地址等.(这些信息,打包的时候就知道,如果不知道,可以把ipa解压,从解压的info.plist里面去获取,填到自己创建的install.plist里面)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>items</key>
<array>
<dict>
<key>assets</key>
<array>
<dict>
<key>kind</key>
<string>software-package</string>
<key>url</key>
<string>http://192.168.**.***/test.ipa</string>
</dict>
</array>
<key>metadata</key>
<dict>
<key>bundle-identifier</key>
<string>必须和bundleidentifier一样</string>
<key>bundle-version</key>
<string>版本号</string>
<key>kind</key>
<string>software</string>
<key>releaseNotes</key>
<string>(可以随意填)</string>
<key>title</key>
<string>App名称</string>
</dict>
</dict>
</array>
</dict>
</plist>

添加配置信息

我们把刚刚建好的plist文件(这里取名为install.plist)、ipa包、ca证书放到Web服务器的文件目录下,然后修改index.html中的内容。
(index.html内容):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>应用名字</title>
</head>
<body>
<h1 style="font-size:40pt">iOS应用OTA安装<h1/>
<h1 style="font-size:40pt">
<a title="iPhone" href="itms-services://?action=download-manifest&url=https://192.168.**.***/install.plist">Iphone Download</a>
<h1/>
<a title="iPhone" href="http://192.168.**.***/ca.crt">ssl 证书安装</a>
<h1/>
</body>
</html>

我们用iphone打开浏览器,输入本地服务器的地址,然后再点击Download,哈哈,是不是已经弹出对话框询问需要安装了??
Oops, 弹出的框是”Cannot connect to *“
怎么办? 服务器没有配置https.
我们用openssl来生成

1
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout ***.key -out ***.crt

代码中添加:

1
2
3
4
5
6
7
8
9
10
11
12
from flask import Flask
from OpenSSL import SSL

import os

context = SSL.Context(SSL.SSLv23_METHOD)
cer = os.path.join(os.path.dirname(__file__), 'resources/***.crt')
key = os.path.join(os.path.dirname(__file__), 'resources/***.key')

if __name__ == '__main__':
context = (cer, key)
app.run( host='0.0.0.0', port=5000, debug = True, ssl_context=context)

解决了SSL问题,重新启动服务,就可以下载了。
然后扩展一下web service 功能(如二维码),做美观一下。

参考:

iOS 证书申请和使用详解

内网安装

Python Flask API in HTTPS

Snake's Home

SSL on flask

發表於 2016-10-15 | 分類於 python

比较喜欢flask这个框架,轻便好用。
在搭建web时,有时候需要用到https.作者在搭建IOS分发的时候,itms-services 就需要https.
本文就以自己搭建的做个说明。

Before starting a server with SSL, you need to create private key and a certificate.

1) .key –> private key
2) .ct –>Self signed certificate

eg: 在主路径下建一个文件夹来存认证文件

1
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout anderson.key -out anderson.crt

代码应该是这样子的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from flask import Flask
from OpenSSL import SSL

import os

context = SSL.Context(SSL.SSLv23_METHOD)
cer = os.path.join(os.path.dirname(__file__), 'resources/anderson.crt')
key = os.path.join(os.path.dirname(__file__), 'resources/anderson.key')

app = Flask(__name__)

@app.route('/')
def hello_world():
return 'Hello World!'

if __name__ == '__main__':
context = (cer, key)
app.run( host='0.0.0.0', port=5000, debug = True, ssl_context=context)

运行起来:

1
2
3
* Running on https://0.0.0.0:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!

页面打开,就有https了。

参考:
Python Flask API in HTTPS

给Rails加上https支持

Snake's Home

flask之mysql数据库连接

發表於 2016-09-20 | 分類於 python

结构

1
2
3
4
5
6
7
8
9
10
├── __init__.py
├── application
│   ├── __init__.py
│   ├── app.py
│   ├── models.py
│   └── templates
│   ├── index.html
│   ├── layout.html
│   └── show.html
├── manage.py

安装

1
2
3
4
flask
flask-Script
flask-SQLAlchemy
MySQL-python

manage.py

1
2
3
4
5
6
7
8
# encoding=utf-8

from flask_script import Manager
from application.app import app
manager = Manager(app)
app.config['DEBUG'] = True
if __name__ == '__main__':
manager.run()

app.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#encoding=utf-8
from flask import Flask
from flask import render_template
from flask import url_for
from flask import redirect
from flask import request,flash
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
track_modifications = app.config.setdefault('SQLALCHEMY_TRACK_MODIFICATIONS', True)
app.config['SQLALCHEMY_DATABASE_URI']="mysql://root:@localhost:3306/message"
db = SQLAlchemy(app)
SECERT_KEY="a secret key"
app.config.from_object(__name__)
app.secret_key=app.config['SECERT_KEY']
from models import Message

@app.route('/',methods=['POST','GET'])
def index():
if request.method == 'POST':
name = request.form['name']
email = request.form['email']
content = request.form['content']
title = request.form['title']
mess = Message(name=name,email=email,content=content,title=title)
db.session.add(mess)
db.session.commit()
flash("Add Message Sucess!!")
#return name+email+content+title
return redirect(url_for("index"))
else:
mess = Message.query.all()
return render_template("index.html",message=mess)

@app.route('/show/<int:id>')
def show(id):
mess = Message.query.filter_by(id=id).first()
if mess !=None:
return render_template("show.html",message=mess)
else:
return redirect(url_for("index"))

models.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#encoding=utf-8
from app import db
from datetime import datetime

class Message(db.Model):
__tablename__ ="message"
id = db.Column(db.Integer,primary_key=True)
name = db.Column(db.String(200))
time = db.Column(db.DateTime,default=datetime.now())
content = db.Column(db.String(255))
email = db.Column(db.String(100))
title = db.Column(db.String(255))

def __init__(self,name,content,email,title):
self.name = name
self.content = content
self.email = email
self.title = title

def __repr__(self):
return "<User:%s>" %self.name


if __name__=="__main__":
db.create_all()

layout.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>留言板</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href={{ url_for('static',filename="css/bootstrap.min.css") }} rel="stylesheet" media="screen">
<script src={{ url_for('static',filename="js/jquery.js") }}></script>
<script src={{ url_for('static',filename="js/bootstrap.min.js") }}></script>
</head>
<body>
{% block body %}
{% endblock %}
</body>
<html>

index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
{% extends "layout.html" %}
{% block body %}
<div class="row">
<div class="span6 offset6">
{% for mess in get_flashed_messages() %}

<div class="alert alert-info">
{{ mess }}
</div>
{% endfor %}
</div>

</div>
<script language="javascript">
function checkinput()
{

var oName=document.getElementById('name');
if(oName.value == ""){
alert("姓名不能为空");
return false;
}
var ocontent=document.getElementById('content');
if(ocontent.value == ""){
alert("内容不能为空");
return false;
}
var otitle=document.getElementById('title');
if(otitle.value == ""){
alert("标题不能为空");
return false;
}
var oemail=document.getElementById('email');
if(oemail.value == ""){
alert("邮箱不能为空");
return false;
}
return true;
}
</script>

<div class="container">
<div class="span8 offset2">
<h2>留言板</h2>
<table class="table table-hover">
<tr>
<th>姓名</th>
<th>留言主题</th>
<th>留言时间</th>
<th>留言邮箱</th>
<tr>
{% for i in message %}
<tr>
<td>{{ i.name }}</td>
<td><a href="/show/{{ i.id }}">{{ i.title}}</a></td>
<td>{{ i.time }}</td>
<td>{{ i.email }}</td>
</tr>
{% endfor %}
</table>
</div>
</div>
<div class="container">
<div class="span12">
<hr>
</div>
</div>
<div class="container">
<div class="span6 offset3">
<form class="form-horizontal" role="form" action="{{ url_for("index") }}" method=post onsubmit="return checkinput();">
<div class="form-group">
<label class="col-sm-4 control-label">姓名</label>
<div class="col-sm-8">
<input type=text name="name" id="name" />
</div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">留言主题</label>
<div class="col-sm-8">
<input type=text name="title" id="title" />
</div>
</div>

<div class="form-group">
<label class="col-sm-4 control-label">留言内容</label>
<div class="col-sm-8">
<input type=text name="content" id="content" />
</div>
</div>

<div class="form-group">
<label class="col-sm-4 control-label">留言信箱</label>
<div class="col-sm-8">
<input type=text name="email" id="email" />
</div>
</div>
<div class="span6 offset3">
<input type=submit value="提交" name="sub"/>
</div>
</div>
</form>
</div>
</div>
{% endblock %}

show.html

1
2
3
4
5
{% extends "layout.html" %}
{% block body %}
<h3>{{ message.title }}<h3>
<p class="less">{{ message.content }}</p>
{% endblock %}

数据库操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ mysql.server start
$ mysql -h localhost -u root -p
show databases;
crate database Message;
use message;
show tables;


#mysql> CREATE TABLE message(
# -> id INT NOT NULL AUTO_INCREMENT,
# -> name VARCHAR(200) NOT NULL,
# -> time DATE,
# -> content VARCHAR(255) NOT NULL,
# -> email VARCHAR(100) NOT NULL,
# -> title VARCHAR(255) NOT NULL,
# -> PRIMARY KEY ( id )
# -> );

mysql查看表结构命令,如下:

desc 表名;
show columns from 表名;
describe 表名;
show create table 表名;

then run:

➜ testdb python manage.py runserver

Snake's Home

一次精准测试实践

發表於 2016-09-01 | 分類於 python

本文发表在公众号: 软件测试精品

敏捷模式下迭代频繁,回归测试时总是不知道变动的范围。Dev 有的时候也不知道他改了哪些东西,影响到哪些节点,或者是很多人改的,彼此不知道。遇到有代码洁癖的,改了别人的代码,大家都不知道。通常情况是,要么测试范围定小了,遗漏了;要么测试范围过大,付出过多代价。每次回归,Tester 心里总没底,生怕漏了哪里。如何才能准确定位到变更范围呢?

作者尝试通过对source code 的变动,来达到了解应用的功能模块变化范围。从而制定回归范围和smoke范围。现在大多数公司用git, 本文就以git 为例。在git 中,用这条命令,就可以查看source change log.

1
git whatchanged

例如:

1
git whatchanged --since='2 weeks ago'

就可以得到最近2周所有commit 的信息。
首先对comments 进行统计。进行词频分析。

1
2
3
4
5
6
7
8
9
def get_words_dict(target):      
table = {}
line, number = re.subn(r"\W", " ", target)
for word in line.split():
if table.has_key(word):
table[word] += 1
else:
table[word] = 1
return table

去除无效的词,自己定义一个list:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
remove_words = ['the', 'a', 'bug', 'to', 'of', 'so', 'one', 'more', 'we', 'Update', 'app', 'our', 'issue','want', 'work']
def remove_dict(obj, key):
if key in obj.keys():
obj.pop(key)for word in remove_words:
remove_dict(words_number, word)
这个词库可以根据需要增加。然后生成一张图表:

def get_words_graphic(wordlist):
keylist = wordlist.keys()
vallist = wordlist.values()
barwidth = 0.3
xVal = numpy.arange(len(keylist))
plt.xticks(xVal + barwidth / 2.0, keylist, rotation=90)
plt.bar(xVal, vallist, width=barwidth, color='y')
plt.title(u'词频分析图')
plt.show()

为了防止图片展示不清楚,同时还写一份log.
这样,就可以大概知道改动了。如果懂些代码,可以深入到代码里面去看。或者拉着Dev一起看,确定最终范围。当然如果Dev的comments 写得够清楚,文件命名很规范,那就更好了。还可以辅助sourcecode 界面工具辅助查看。
这样,每次回归就不会那么没有底了。

完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#!/usr/bin/python
# coding=utf-8
import subprocess
import sys
import urllib2
import time
import json
import re
import numpy
import matplotlib.pyplot as plt
from collections import Counter

__author__ = 'anderson'

reload(sys)
sys.setdefaultencoding('utf-8')

log_file = r'gitfiles.log'
start_time_stamp = time.strftime("%Y%m%d%H%M%S")

def write_log(file_name, content):
file_object = open(file_name, 'a+')
file_object.write(content)
file_object.close()


def get_comments(obj):
sentence = obj[obj.index(" "):].strip().rstrip("\n")
return sentence


def get_file(obj):
if obj.rfind("..."):
file = obj[obj.rindex("...") + 4:].strip().rstrip("\n")
return file


def get_git_info():
commit_comments = []
file_comments = []
commit_pattern = re.compile(r'^\d+\w+', re.IGNORECASE)
file_pattern = re.compile(r'^:\d+\w+', re.IGNORECASE)
cmd = "git whatchanged --since='2 weeks ago' --pretty=oneline"
lines = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout.readlines()
for line in lines:
if re.search(commit_pattern, line):
commit = get_comments(line)
commit_comments.append(commit)
write_log(log_file, commit)
write_log(log_file, "\n")
elif re.search(file_pattern, line):
commit_file = get_file(line)
file_comments.append(commit_file)
write_log(log_file, commit_file)
write_log(log_file, "\n")
return commit_comments, file_comments


def get_file_statistic(target):
table = Counter(target)
return table


def get_words_dict(target):
table = {}
line, number = re.subn(r"\W", " ", target)
for word in line.split():
if table.has_key(word):
table[word] += 1
else:
table[word] = 1
return table


def get_words_graphic(wordlist):
keylist = wordlist.keys()
vallist = wordlist.values()
barwidth = 0.3
xVal = numpy.arange(len(keylist))
plt.xticks(xVal + barwidth / 2.0, keylist, rotation=90)
plt.bar(xVal, vallist, width=barwidth, color='y')
plt.title(u'词频分析图')
plt.show()


def get_file_graphic(wordlist):
keylist = wordlist.keys()
vallist = wordlist.values()
barwidth = 0.3
xVal = numpy.arange(len(keylist))
plt.yticks(xVal + barwidth / 2.0, keylist, rotation=0)
plt.barh(xVal, vallist, height=barwidth, color='b')
plt.title(u'文件分析图')
plt.show()


def remove_dict(obj, key):
if key in obj.keys():
obj.pop(key)


if __name__ == '__main__':

write_log(log_file, start_time_stamp)
write_log(log_file, "\n")

comments, files = get_git_info()

remove_words = ['the', 'a', 'bug', 'to', 'of', 'so', 'one', 'more', 'we', 'ME', 'Update', 'app', 'our', 'issue',
'want', 't', 'work']

commit_str = ''.join(comments)
words_number = get_words_dict(commit_str)

for word in remove_words:
remove_dict(words_number, word)

fine_number = get_file_statistic(files)
get_words_graphic(words_number)
get_file_graphic(fine_number)
Snake's Home

matplotlib 画图

發表於 2016-08-16 | 分類於 python

Matplotlib是一个Python工具箱,用于科学计算的数据可视化。借助它,Python可以绘制如Matlab和Octave多种多样的数据图形。

圆饼图-pie()

pie()最基本的输入数据是一个数组。它可以根据数组元素的比例绘制扇形,因此不必要事先计算好百分比。

1
2
3
4
5
6
7
import matplotlib.pyplot as plt

rate = [1, 7, 3, 9]

plt.pie(rate)

plt.show()

样式

有几个最常用的参数可以控制绘图样式:

colors - 数组,扇形颜色
explode - 数组,扇形偏离圆心的距离
labels - 数组,扇形的标签

首先,让我们为饼图设定颜色,并让第三个扇形抽离出来:

1
2
3
4
5
6
7
8
9
import matplotlib.pyplot as plt

rate = [1, 7, 3, 9]
explode = [0, 0, 0.1, 0]
colors = [‘c‘, ‘m‘, ‘y‘, ‘g‘]

plt.pie(rate, explode=explode, colors=colors)

plt.show()

然后,要为每个扇形添加标签:

1
2
3
4
5
6
7
8
9
10
import matplotlib.pyplot as plt

rate = [1, 7, 3, 9]
explode = [0, 0, 0.1, 0]
colors = [‘c‘, ‘m‘, ‘y‘, ‘g‘]
labels = [‘Apple‘, ‘Pear‘, ‘Peach‘, ‘Orange‘]

plt.pie(rate, explode=explode, colors=colors, labels=labels)

plt.show()

百分比

如何显示百分比数值呢?我们需要使用autopct参数:

None,不显示百分比
格式字符串,如‘%d percent‘,显示“40 percent”形式的百分比
方法,调用方法输出百分比

1
2
3
4
5
6
7
8
9
10
import matplotlib.pyplot as plt

rate = [1, 7, 3, 9]
explode = [0, 0, 0.1, 0]
colors = [‘c‘, ‘m‘, ‘y‘, ‘g‘]
labels = [‘Apple‘, ‘Pear‘, ‘Peach‘, ‘Orange‘]

plt.pie(rate, explode=explode, colors=colors, labels=labels, autopct=‘%d%%‘)

plt.show()

autopct=‘%d%%‘表示我们将百分比以整数(%d)形式输出,后缀是百分号‘%‘。在格式化字符串中,百分号要用‘%%‘转义字符表示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pylab import *

# make a square figure and axes
figure(1, figsize=(6,6))
ax = axes([0.1, 0.1, 0.8, 0.8])

fracs = [45, 30, 25] #每一块占得比例,总和为100
explode=(0, 0, 0.08) #离开整体的距离,看效果
labels = 'Hogs', 'Dogs', 'Logs' #对应每一块的标志

pie(fracs, explode=explode, labels=labels,
autopct='%1.1f%%', shadow=True, startangle=90, colors = ("g", "r", "y"))
# startangle是开始的角度,默认为0,从这里开始按逆时针方向依次展开

title('Raining Hogs and Dogs') #标题

show()

柱形图-bar()
什么是最简单的bar,看如下语句你就知道她有多么简单了:

1
2
3
4
import matplotlib.pyplot as plt  

plt.bar(left = 0,height = 1)
plt.show()

left:柱形的左边缘的位置,如果我们指定1那么当前柱形的左边缘的x值就是1.0了

height:这是柱形的高度,也就是Y轴的值了

left,height除了可以使用单独的值(此时是一个柱形),也可以使用元组来替换(此时代表多个矩形)。例如,下面的例子:

1
2
3
4
import matplotlib.pyplot as plt 

plt.bar(left = (0,1),height = (1,0.5))
plt.show()

当然,可能你还觉得这两个矩形“太胖”了。此时我们可以通过指定bar的width参数来设置它们的宽度。

此时又来需求了,我需要标明x,y轴的说明。比如x轴是性别,y轴是人数。实现也很简单,看代码:

1
2
3
4
5
6
import matplotlib.pyplot as plt 

plt.xlabel(u'性别')
plt.ylabel(u'人数')
plt.bar(left = (0,1),height = (1,0.5),width = 0.35)
plt.show()

接下来,让我们在x轴上的每个bar进行说明。比如第一个是“男”,第二个是“女”。

我们可以通过直接指定bar方法里面的align=”center”就可以让文字居中了。

接下来,我们还可以给图标加入标题。

plt.title(u”性别比例分析”)
当然,还有图例也少不掉:

1
2
3
4
5
6
7
8
9
10
11
12
13
import matplotlib.pyplot as plt 

plt.xlabel(u'性别')
plt.ylabel(u'人数')


plt.title(u"性别比例分析")
plt.xticks((0,1),(u'男',u'女'))
rect = plt.bar(left = (0,1),height = (1,0.5),width = 0.35,align="center")

plt.legend((rect,),(u"图例",))

plt.show()

柱状图-bar()

bar()函数可以绘制各种样式的柱状图,barh()则可绘制水平方向的柱状图。两个方法除了绘图方向外,其他属性和用法的是相同的。

数据和坐标

bar()至少需要两个数组left和height:left是每个柱向左到坐标原点的距离;height是每个柱的高度。

1
2
3
4
5
6
7
8
mport matplotlib.pyplot as plt

left = [0, 1, 2, 3, 4, 5]
height = [0.1, 0.2, 0.2, 0.3, 0.2, 0.1]

plt.bar(left, height)

plt.show()

样式

bar()函数的参数可以控制柱状图的多种样式,最常用的有:

width,第3个参数,柱的宽度。可以是一个常数或数组。数组将对每条柱设定不同的值。

bottom,第4个参数,柱底部的y轴坐标。可以是一个常数或数组。数组将对每条柱设定不同的值。

color,关键字参数,柱的填充颜色。可以是一个常数或数组。数组将对每条柱设定不同的值。

edgecolor,关键字参数,柱的边框颜色。

linewidth,关键字参数,边框宽度。

xerr,yerr,关键字参数,x和y轴误差线。

ecolor,关键字参数,误差线颜色。

align,关键字参数,设定柱的对齐方式。‘edge‘将x坐标设为左边界,‘center‘将x坐标设为中轴线。

下面的柱状图要表达一件商品12个月的销量:

1
2
3
4
5
6
7
8
import matplotlib.pyplot as plt

month = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
sales = [112, 105, 111, 109, 103, 110, 113, 112, 108, 106, 111, 114]

plt.bar(month, sales, 0.5, color=‘y‘, edgecolor=‘g‘, linewidth=3, align=‘center‘)

plt.show()

我们设置柱宽度0.5,填充颜色‘y‘(黄色),边框颜色‘g‘(绿色),线框宽度3,对齐方式为‘center‘

1
2
3
4
5
6
7
8
9
import matplotlib.pyplot as plt

month = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
sales = [12, 5, 11, 9, 3, 10, 13, 12, 8, 6, 11, 14]
colors = [‘g‘, ‘g‘, ‘g‘, ‘m‘, ‘m‘, ‘m‘, ‘y‘, ‘y‘, ‘y‘, ‘c‘, ‘c‘, ‘c‘]

plt.bar(month, sales, 0.5, 100, color=colors, linewidth=0, align=‘center‘)

plt.show()

多段柱图和水平柱图

绘制多段柱图的原理是:先后绘制多张柱图,依次重叠在上方,如果后面绘制的柱图比前者的柱图短,就可以显示出前者长出的部分。

1
2
3
4
5
6
7
8
9
10
11
12
import matplotlib.pyplot as plt

month = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
cost = [8, 7, 9, 9, 3, 10, 10, 12, 8, 6, 11, 10]
profit = [12, 11, 11, 13, 5, 11, 13, 15, 10, 9, 12, 13]

bar1 = plt.bar(month, profit, 0.5, color=‘y‘, linewidth=0, align=‘center‘)
bar2 = plt.bar(month, cost, 0.5, color=‘g‘, linewidth=0, align=‘center‘)

plt.legend( (bar1[0], bar2[0]), (‘Profits‘, ‘Costs‘) )

plt.show()

最后,让我们来绘制一个水平柱图,只需将bar()替换成barh():

1
2
3
4
5
6
7
8
9
10
11
12
import matplotlib.pyplot as plt

month = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
cost = [8, 7, 9, 9, 3, 10, 10, 12, 8, 6, 11, 10]
profit = [12, 11, 11, 13, 5, 11, 13, 15, 10, 9, 12, 13]

bar1 = plt.barh(month, profit, 0.5, color=‘y‘, linewidth=0, align=‘center‘)
bar2 = plt.barh(month, cost, 0.5, color=‘g‘, linewidth=0, align=‘center‘)

plt.legend( (bar1[0], bar2[0]), (‘Profits‘, ‘Costs‘) )

plt.show()

折线图和散点图-plot()
plot()函数可以绘制折线图和折线图,取决于您使用的参数。

数据和坐标

1
2
3
4
5
6
7
8
import matplotlib.pyplot as plt

x = [0, 1, 2, 3, 4, 5]
y = [0.1, 0.2, 0.2, 0.3, 0.2, 0.1]

plt.plot(x, y)

plt.show()

绘制折线图/散点图需要一系列关键点。x是关键点的x轴坐标,y是关键点的y轴坐标。

x轴坐标是可以缺省的:

plot(y)
y是一个一维数组,是折线图关键点的y轴坐标。而x轴坐标没有给出,会默认以[0,1,2,…]的常数列作为x轴坐标。

样式

散点图和折线图只是线条样式的差异,我们可以通过简单的参数设置线条的样式和颜色。样式和颜色参数都是一个或多个字符构成的字符串。

既可以单独设定颜色或样式,如‘g‘代表绿色,‘-‘代表实线
也可以同时设定样式和颜色,比如‘–g‘代表绿色虚线,其中‘–‘代表虚线,‘g‘代表绿色
样式字符串中的参数字符是无顺序的,‘g–‘和‘–g‘的意义是相同的
也可以同时设定折线和散点,如‘-or‘代表红色实线+圆圈

1
2
3
4
5
6
7
8
import matplotlib.pyplot as plt

x = [0, 1, 2, 3, 4, 5]
y = [0.1, 0.2, 0.2, 0.3, 0.2, 0.1]

plt.plot(x, y, ‘-or‘)

plt.show()

折线样式:

参数 样式

‘-‘ 实线

‘–‘ 虚线

‘-.‘ 线-点

‘:‘ 点虚线

散点样式:

参数 样式

‘.‘ 实心点

‘o‘ 圆圈

‘,‘ 一个像素点

‘x‘ 叉号

‘+‘ 十字

‘*‘ 星号

‘^‘ ‘v‘ ‘<‘ ‘>‘ 三角形(上下左右)

‘1‘ ‘2‘ ‘3‘ ‘4‘ 三叉号(上下左右)

颜色:

参数 颜色

‘b‘ 蓝

‘g‘ 绿

‘r‘ 红

‘c‘ 青

‘m‘ 品红

‘y‘ 黄

‘k‘ 黑

‘w‘ 白

更多样式参见

您也可以不必使用样式字符串,而直接使用参数名,这种方式更加灵活:

1
plot(x, y, color=‘green‘, linestyle=‘dashed‘, marker=‘o‘, markerfacecolor=‘blue‘, markersize=12)

多条线

我们可以在一个坐标中绘制多条折线或散点,plot(第一条线的参数,第二条线的参数…):

1
2
3
4
5
6
7
8
9
import matplotlib.pyplot as plt

x = [0, 1, 2, 3, 4, 5]
y = [0.1, 0.2, 0.2, 0.3, 0.2, 0.1]
y2 = [0.2, 0.2, 0.3, 0.2, 0.3, 0]

plt.plot(x, y, ‘b‘, x, y2, ‘g‘)

plt.show()

更多请参考

样式

12…7
Snake

Snake

不要被困难击倒,除非你躺着睡大觉

69 文章
17 分類
57 標籤
© 2017 Snake
由 Hexo 強力驅動
主題 - NexT.Muse