├── api
├── __init__.py
├── app_utils.py
└── api_utils.py
├── db
├── __init__.py
├── mysql_utils.py
├── dsidx_dbs.py
├── other_dbs.py
├── api_dbs.py
└── sqlite_utils.py
├── dump
├── __init__.py
├── codesign_utils.py
├── class_dump_utils.py
└── otool_utils.py
├── utils
├── __init__.py
├── lib
│ ├── __init__.py
│ └── xlsxwriter
│ │ ├── __init__.py
│ │ ├── compatibility.py
│ │ ├── chart_radar.py
│ │ ├── chart_area.py
│ │ ├── chart_doughnut.py
│ │ ├── chart_line.py
│ │ ├── relationships.py
│ │ ├── chart_column.py
│ │ ├── chart_stock.py
│ │ ├── sharedstrings.py
│ │ ├── chart_bar.py
│ │ ├── chartsheet.py
│ │ ├── table.py
│ │ ├── comments.py
│ │ ├── app.py
│ │ ├── core.py
│ │ ├── chart_pie.py
│ │ ├── contenttypes.py
│ │ ├── xmlwriter.py
│ │ ├── compat_collections.py
│ │ └── theme.py
├── .DS_Store
└── utils.py
├── app
├── dbs
│ ├── inc
│ │ ├── __init__.py
│ │ ├── Mongo.py
│ │ ├── Redis.py
│ │ └── Mysql.py
│ ├── __init__.py
│ ├── main_dbs.py
│ └── test_dbs.py
├── others
│ ├── __init__.py
│ └── tasks.py
├── utils
│ ├── __init__.py
│ ├── jinja2_ex
│ │ ├── __init__.py
│ │ └── template_filter.py
│ ├── DateUtil.py
│ ├── StringUtil.py
│ ├── CJsonEncoder.py
│ ├── LogUtil.py
│ ├── OtherUtil.py
│ ├── PathUtil.py
│ ├── RequestUtil.py
│ └── IpaParse.py
├── views
│ ├── __init__.py
│ └── main_views.py
├── wraps
│ ├── __init__.py
│ ├── allow_request_wrap.py
│ ├── singleton_wrap.py
│ ├── async_task_wrap.py
│ ├── login_wrap.py
│ ├── trace_wrap.py
│ ├── mysql_escape_warp.py
│ └── db_connect_warp.py
├── static
│ ├── res
│ │ ├── css
│ │ │ ├── RAEDME.md
│ │ │ ├── styles.css
│ │ │ └── dropzone.min.css
│ │ ├── img
│ │ │ └── RAEDME.md
│ │ ├── js
│ │ │ ├── RAEDME.md
│ │ │ └── ios_private.js
│ │ └── favicon.ico
│ └── upload
│ │ └── RAEDME.md
├── __init__.py
└── templates
│ └── main
│ └── index_page.html
├── requirements.txt
├── .DS_Store
├── class-dump
├── class_dump_z
├── iphone_armv6
│ ├── README
│ └── class-dump-z
├── win_x86
│ ├── .DS_Store
│ └── class-dump-z.exe
├── linux_x86
│ ├── .DS_Store
│ ├── class-dump-z
│ └── README
├── mac_x86
│ └── class-dump-z
└── README
├── screenshot
├── web_screenshot.png
├── excel_report_detail.png
└── excel_report_outline.png
├── run_web.py
├── run_web
├── .project
├── .pydevproject
├── .settings
└── org.eclipse.core.resources.prefs
├── .gitignore
├── config.py
├── README.md
└── iOS_private.py
/api/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/db/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dump/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/utils/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/dbs/inc/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/others/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/utils/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/views/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/wraps/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/utils/lib/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/static/res/css/RAEDME.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/static/res/img/RAEDME.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/static/res/js/RAEDME.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/static/upload/RAEDME.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/utils/jinja2_ex/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | Flask
2 | gunicorn
3 | gevent
4 | macholib
--------------------------------------------------------------------------------
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEaseGame/iOS-private-api-checker/HEAD/.DS_Store
--------------------------------------------------------------------------------
/class-dump:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEaseGame/iOS-private-api-checker/HEAD/class-dump
--------------------------------------------------------------------------------
/app/dbs/__init__.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年6月16日
4 |
5 | @author: atool
6 | '''
7 |
--------------------------------------------------------------------------------
/db/mysql_utils.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年10月27日
4 |
5 | @author: atool
6 | '''
7 |
--------------------------------------------------------------------------------
/utils/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEaseGame/iOS-private-api-checker/HEAD/utils/.DS_Store
--------------------------------------------------------------------------------
/class_dump_z/iphone_armv6/README:
--------------------------------------------------------------------------------
1 | You must install the "pcre" package from Cydia to run class-dump-z on the iPhoneOS.
--------------------------------------------------------------------------------
/app/static/res/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEaseGame/iOS-private-api-checker/HEAD/app/static/res/favicon.ico
--------------------------------------------------------------------------------
/app/wraps/allow_request_wrap.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年6月17日
4 |
5 | @author: atool
6 | '''
7 |
8 |
--------------------------------------------------------------------------------
/utils/lib/xlsxwriter/__init__.py:
--------------------------------------------------------------------------------
1 | __version__ = '0.7.3'
2 | __VERSION__ = __version__
3 | from .workbook import Workbook
4 |
--------------------------------------------------------------------------------
/class_dump_z/win_x86/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEaseGame/iOS-private-api-checker/HEAD/class_dump_z/win_x86/.DS_Store
--------------------------------------------------------------------------------
/screenshot/web_screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEaseGame/iOS-private-api-checker/HEAD/screenshot/web_screenshot.png
--------------------------------------------------------------------------------
/class_dump_z/linux_x86/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEaseGame/iOS-private-api-checker/HEAD/class_dump_z/linux_x86/.DS_Store
--------------------------------------------------------------------------------
/class_dump_z/mac_x86/class-dump-z:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEaseGame/iOS-private-api-checker/HEAD/class_dump_z/mac_x86/class-dump-z
--------------------------------------------------------------------------------
/class_dump_z/linux_x86/class-dump-z:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEaseGame/iOS-private-api-checker/HEAD/class_dump_z/linux_x86/class-dump-z
--------------------------------------------------------------------------------
/screenshot/excel_report_detail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEaseGame/iOS-private-api-checker/HEAD/screenshot/excel_report_detail.png
--------------------------------------------------------------------------------
/screenshot/excel_report_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEaseGame/iOS-private-api-checker/HEAD/screenshot/excel_report_outline.png
--------------------------------------------------------------------------------
/class_dump_z/iphone_armv6/class-dump-z:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEaseGame/iOS-private-api-checker/HEAD/class_dump_z/iphone_armv6/class-dump-z
--------------------------------------------------------------------------------
/class_dump_z/win_x86/class-dump-z.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEaseGame/iOS-private-api-checker/HEAD/class_dump_z/win_x86/class-dump-z.exe
--------------------------------------------------------------------------------
/run_web.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年11月05日
4 | iOS private api检查 Web启动入口
5 | @author: atool
6 | '''
7 |
8 |
9 | from app import app
10 | if __name__ == '__main__':
11 | app.run('0.0.0.0', 9527, debug = True, threaded = True)
--------------------------------------------------------------------------------
/class_dump_z/README:
--------------------------------------------------------------------------------
1 | Source code can be found in various directories inside http://code.google.com/p/networkpx/source/browse/trunk/hk.kennytm.Peace/.
2 |
3 | This program is license in GPLv3. Read the LICENSE file for detail.
4 |
5 | Platform-specific info can be found in the README files in the respective directories.
--------------------------------------------------------------------------------
/app/utils/DateUtil.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年8月24日
4 |
5 | @author: atool
6 | '''
7 | import datetime
8 |
9 |
10 | #当前时间,可用于mysql datetime
11 | def now_datetime():
12 | return datetime.datetime.now().strftime("%y-%m-%d %H:%M:%S")
13 |
14 |
15 | if __name__ == '__main__':
16 | print now_datetime()
--------------------------------------------------------------------------------
/app/dbs/main_dbs.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年6月16日
4 | main模块涉及的数据库操作
5 | 可以是mysql,也可以是mongodb
6 | 使用什么数据库,什么orm均不限制
7 | @author: atool
8 | '''
9 |
10 |
11 | def get_user_by_id(u_id):
12 | '''
13 | get the user information
14 | '''
15 | #执行sql,获得数据,返回给views层使用
16 | return {'name':'atool', 'sex': 1}
--------------------------------------------------------------------------------
/run_web:
--------------------------------------------------------------------------------
1 | pids=$(ps -ef|grep gunicorn|grep run_web|awk {'print $2,$3'})
2 | pidsArray=($pids)
3 |
4 | len=${#pidsArray[*]}
5 | for ((index=0;index<$len;index+=1));do
6 | tmp=`expr $index+1`
7 | echo ${pidsArray[$index]}
8 | kill -9 ${pidsArray[$index]}
9 | done
10 | sleep 1
11 | gunicorn --worker-class=gevent -w 4 -b 0.0.0.0:9527 run_web:app
12 |
--------------------------------------------------------------------------------
/app/wraps/singleton_wrap.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年8月21日
4 | 单利模式装饰器
5 | @author: atool
6 | '''
7 | def singleton(cls, *args, **kw):
8 | instances = {}
9 | def _singleton():
10 | if cls not in instances:
11 | instances[cls] = cls(*args, **kw)
12 | return instances[cls]
13 | return _singleton
--------------------------------------------------------------------------------
/app/others/tasks.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年6月16日
4 | all tasks
5 | @author: atool
6 | '''
7 | from app.wraps.async_task_wrap import async_task
8 |
9 |
10 | @async_task
11 | def count_to_10000():
12 | '''
13 | test async_task, count to 1w
14 | 注意:在uwsgi部署情况下,不能执行异步任务,所有任务在一次请求完成之后,全部释放
15 | '''
16 | for i in xrange(10000):
17 | print i
18 |
19 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | iOS-private-api-checker
4 |
5 |
6 |
7 |
8 |
9 | org.python.pydev.PyDevBuilder
10 |
11 |
12 |
13 |
14 |
15 | org.python.pydev.pythonNature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/wraps/async_task_wrap.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年6月16日
4 |
5 | @author: atool
6 | '''
7 | from functools import wraps
8 | from threading import Thread
9 |
10 |
11 | def async_task(f):
12 | '''
13 | wrap with this, the function will be async
14 | use at task which need long time to finish
15 | '''
16 | @wraps(f)
17 | def wrapper(*args, **kwargs):
18 | thr = Thread(target=f, args=args, kwargs=kwargs)
19 | thr.start()
20 | return wrapper
--------------------------------------------------------------------------------
/.pydevproject:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | /${PROJECT_DIR_NAME}
5 | /${PROJECT_DIR_NAME}/tmp
6 |
7 | python 2.7
8 | Default
9 |
10 |
--------------------------------------------------------------------------------
/app/utils/StringUtil.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年6月16日
4 |
5 | @author: atool
6 | '''
7 | import time
8 | import datetime
9 | import random
10 |
11 | def is_empty(s):
12 | if s == None or s == '':
13 | return True
14 | return False
15 |
16 |
17 | def get_unique_str():
18 | #随机的名字,可以用于上传文件等等不重复,但有一定时间意义的名字
19 | datetime_str = time.strftime('%Y%m%d%H%M%S',time.localtime())
20 | return datetime_str + str(datetime.datetime.now().microsecond / 1000) + str(random.randint(0, 1000))
--------------------------------------------------------------------------------
/app/utils/CJsonEncoder.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年1月27日
4 |
5 | @author: atool
6 | '''
7 | from datetime import date
8 | from datetime import datetime
9 | import json
10 |
11 |
12 | class CJsonEncoder(json.JSONEncoder):
13 | def default(self, obj):
14 | if isinstance(obj, datetime):
15 | return obj.strftime('%Y-%m-%d %H:%M:%S')
16 | elif isinstance(obj, date):
17 | return obj.strftime('%Y-%m-%d')
18 | else:
19 | return json.JSONEncoder.default(self, obj)
--------------------------------------------------------------------------------
/class_dump_z/linux_x86/README:
--------------------------------------------------------------------------------
1 | The Linux version of class-dump-z is compiled in Linux 2.6.31-14-generic i686 with gcc-4.2.4. The following are the dynamic libraries needed:
2 |
3 | linux-gate.so.1 => (0x00bd6000)
4 | libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00a29000)
5 | libm.so.6 => /lib/tls/i686/cmov/libm.so.6 (0x00ea1000)
6 | libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x005f1000)
7 | libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x007f9000)
8 | /lib/ld-linux.so.2 (0x0034f000)
9 |
10 | Usually it will just run fine on x86 and amd64 systems.
--------------------------------------------------------------------------------
/app/wraps/login_wrap.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年1月28日
4 |
5 | @author: atool
6 | '''
7 | from functools import wraps
8 |
9 | from flask import request, redirect, url_for
10 | from flask.globals import session
11 |
12 |
13 |
14 | def login_required(f):
15 | '''
16 | need login wrap
17 | '''
18 | @wraps(f)
19 | def decorated_function(*args, **kwargs):
20 | if 'u_id' not in session or session['u_id'] is None or session['u_id'] == '':
21 | return redirect(url_for('login', next = request.url))
22 | return f(*args, **kwargs)
23 | return decorated_function
--------------------------------------------------------------------------------
/app/wraps/trace_wrap.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年8月21日
4 |
5 | @author: atool
6 | '''
7 |
8 | from functools import wraps
9 | import traceback
10 |
11 | #定义一个trace监控的装饰器
12 | def log_traceback(f):
13 | @wraps(f)
14 | def decorated_function(*args, **kwargs):
15 | #先执行方法,然后写日志
16 | try:
17 | func = f(*args, **kwargs)
18 | return func
19 | except:
20 | #如果出现trace异常,发送到服务器
21 | trace = traceback.format_exc()
22 | print trace
23 | #TODO 将trace写入到文件或者post到服务器中
24 | return func
25 | return decorated_function
--------------------------------------------------------------------------------
/app/dbs/test_dbs.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年8月25日
4 |
5 | @author: atool
6 | '''
7 | from app.dbs.inc.Redis import RedisMysqlCache
8 | from app.dbs.inc.Mysql import Mysql
9 |
10 |
11 | #获取tid的测试信息
12 | def get_test_by_id(test_id, use_redis_cache = True):
13 | sql = "select * from abtest_tests where test_id = %s"
14 | params = (test_id, )
15 |
16 | #该方法你用redis缓存
17 | if use_redis_cache:
18 | return RedisMysqlCache().select_one(sql, params)
19 | else:
20 | return Mysql().exec_select_one(sql, params)
21 |
22 | if __name__ == '__main__':
23 | print get_test_by_id(1)
24 | print get_test_by_id("1", False)
--------------------------------------------------------------------------------
/dump/codesign_utils.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年11月17日
4 |
5 | @author: atool
6 | '''
7 |
8 | import commands
9 |
10 |
11 |
12 | def codesignapp(app_path):
13 | """
14 | Get codesign informatiob included in app
15 | About codesign: https://developer.apple.com/legacy/library/technotes/tn2250/_index.html#//apple_ref/doc/uid/DTS40009933
16 | Args:
17 | Mach-o path
18 | Returns:
19 | the content of codesign
20 | """
21 | cmd = "/usr/bin/codesign -dvvv %s" % app_path
22 | out = commands.getstatusoutput(cmd)
23 | if out and len(out) == 2 and out[0] == 0:
24 | out = out[1]
25 | else:
26 | out = ''
27 | return out
--------------------------------------------------------------------------------
/app/utils/LogUtil.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年8月24日
4 |
5 | @author: atool
6 | '''
7 | from app.utils import PathUtil, DateUtil
8 |
9 | def append_log(log_file, data):
10 | '''
11 | append data to file pathing with filePath
12 | '''
13 | if data:
14 | file_handler = open(PathUtil.log_dir() + log_file, 'a')
15 | file_handler.write(data + '\n')
16 | file_handler.close()
17 |
18 | return True
19 |
20 | #记录非法用户的日志,一般这些用户都是尝试模拟请求的方式往数据库写入信息
21 | def log_invalid(request, ext_text):
22 | log = '%s - - [%s] %s %s %s' % (request.remote_addr, DateUtil.now_datetime(), request.method, request.path, ext_text)
23 | append_log('record_invaild_log.log', log)
--------------------------------------------------------------------------------
/app/utils/OtherUtil.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年8月21日
4 |
5 | @author: atool
6 | '''
7 | import hashlib
8 | import json
9 | from CJsonEncoder import CJsonEncoder
10 |
11 | def md5_salt(s, salt = "ab_test"):
12 | '''
13 | md5 + 盐:即便两个用户使用了同一个密码,由于系统为它们生成的salt值不同,他们的散列值也是不同的。
14 | '''
15 | if s:
16 | return md5(s + salt)
17 | else:
18 | return ''
19 |
20 | def md5(s):
21 | '''
22 | md5
23 | '''
24 | m = hashlib.md5()
25 | m.update(s)
26 | return m.hexdigest()
27 |
28 | def object_2_dict(obj):
29 | '''
30 | py obj to dict
31 | '''
32 | if obj == None:
33 | return {}
34 | return json.dumps(obj, cls = CJsonEncoder)
--------------------------------------------------------------------------------
/app/utils/PathUtil.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年2月5日
4 |
5 | @author: atool
6 | '''
7 | import os
8 | import sys
9 |
10 | def upload_dir():
11 | return _cur_file_dir() + '/app/static/upload/'
12 |
13 | def log_dir():
14 | return _cur_file_dir() + '/log/'
15 |
16 | def default_tmp_dir():
17 | return _cur_file_dir() + '/app/static/tmp/'
18 |
19 | def _cur_file_dir():
20 | #获取脚本路径
21 | path = sys.path[0]
22 | #判断为脚本文件还是py2exe编译后的文件,如果是脚本文件,则返回的是脚本的目录,如果是py2exe编译后的文件,则返回的是编译后的文件路径
23 | if os.path.isdir(path):
24 | return path
25 | elif os.path.isfile(path):
26 | return os.path.dirname(path)
27 |
28 | if __name__ == '__main__':
29 | print _cur_file_dir()
30 |
--------------------------------------------------------------------------------
/app/__init__.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年6月16日
4 |
5 | @author: atool
6 | '''
7 | from flask import Flask
8 |
9 | app = Flask(__name__)
10 | app.secret_key = 'your_session_key_ab_test'
11 | app.config['MAX_CONTENT_LENGTH'] = 1024 * 1024 * 1024
12 |
13 | db_config = {
14 | 'DB_USER': 'dev',
15 | 'DB_PSW': 'dev',
16 | 'DB_NAME': 'ab_test',
17 | 'DB_HOST': '10.246.14.121',
18 | 'DB_PORT': 3306,
19 | 'DB_CHARSET': 'utf8'
20 | }
21 |
22 | redis_config = {
23 | 'RD_PSW': None,
24 | 'RD_HOST': '10.246.14.121',
25 | 'RD_PORT': 6379,
26 | 'RD_CHARSET': 'utf8',
27 | 'TEST_DB': 0,
28 | 'TEMP_DB': 1, #缓存db
29 | 'RECORD_DB': 2 #用户访问db
30 | }
31 |
32 | from app.views import main_views
--------------------------------------------------------------------------------
/dump/class_dump_utils.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年10月27日
4 | class-dump操作类
5 | @author: atool
6 | '''
7 | import subprocess
8 | from utils import utils
9 | class_dump_path = utils.get_clas_dump_path()
10 |
11 | dump_cmd = class_dump_path + " -H %s -o %s" # dump cmd模板字符串
12 |
13 | def dump_framework(frame_path, out_path):
14 | '''
15 | info:使用class-dump来解开framework中的api
16 | '''
17 | cmd = dump_cmd % (frame_path, out_path)
18 | ret = subprocess.call(cmd.split())
19 | if ret != 0:
20 | return frame_path
21 | return ""
22 |
23 | def dump_app(app_path):
24 | '''
25 | get all private variables, properties, and interface name
26 | '''
27 | class_dump = class_dump_path + " %s" % app_path
28 | dump_result = subprocess.check_output(class_dump.split())
29 | return dump_result
--------------------------------------------------------------------------------
/app/utils/RequestUtil.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年8月21日
4 |
5 | @author: atool
6 | '''
7 | #获得参数,post或者get
8 | def get_parameter(request, key, default = None):
9 | '''
10 | info:获得请求参数,包括get和post,其他类型的访问不管
11 | '''
12 | #post参数
13 | if request.method == 'POST':
14 | param = request.form.get(key, default)
15 | #get
16 | elif request.method == 'GET':
17 | param = request.args.get(key, default)
18 | else:
19 | return default
20 |
21 | return param
22 |
23 | #用户IP
24 | def get_request_ip(request):
25 | return request.remote_addr
26 |
27 | #获得用户访问方式
28 | def get_request_method(request):
29 | return request.method
30 |
31 | def get_request_ua(request):
32 | return request.headers.get('User-Agent', '')
33 |
34 | def get_request_accept_lang(request):
35 | request.environ.get('HTTP_ACCEPT_LANGUAGE', '')
36 |
--------------------------------------------------------------------------------
/app/utils/jinja2_ex/template_filter.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年5月6日
4 |
5 | @author: atool
6 | '''
7 | from app import app
8 |
9 | @app.context_processor
10 | def ext_jinja2_processor():
11 | '''
12 | ps:扩展jinja2的内置方法
13 | '''
14 | def str_sub(s, start, end, suffix = None):
15 | '''
16 | str_sub;字符串截断
17 | '''
18 | if suffix:
19 | return s[start:end] + suffix
20 | return s[start:end]
21 |
22 | def str_len(s):
23 | '''
24 | str_len:字符串长度
25 | '''
26 | return len(s)
27 |
28 | def to_str(i):
29 | '''
30 | to_str:将数字转字符串,一些比较的时候会使用到
31 | '''
32 | return str(i)
33 |
34 | def to_round(f, d = 3):
35 | '''
36 | to_round:浮点数小数位数
37 | '''
38 | return round(f, d)
39 |
40 | return dict(str_sub = str_sub, str_len = str_len, to_str = to_str, to_round = to_round)
41 |
--------------------------------------------------------------------------------
/dump/otool_utils.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年10月29日
4 |
5 | @author: atool
6 | '''
7 |
8 | import subprocess
9 | import re
10 |
11 | otool_path = "otool" #otool所在的位置
12 |
13 | otool_cmd = otool_path + " -L %s" # otool cmd模板字符串
14 |
15 | def otool_app(app_path):
16 | """
17 | Get framework included in app
18 | Args:
19 | Mach-o path
20 | Returns:
21 | two sets, one is public framework, one is private framework
22 | """
23 | cmd = otool_cmd % app_path
24 |
25 | out = subprocess.check_output(cmd.split())
26 | pattern = re.compile("PrivateFrameworks\/(\w*)\.framework")
27 | pub_pattern = re.compile("Frameworks\/([\.\w]*)")
28 |
29 | private = set()
30 | public = set()
31 |
32 | for r in re.finditer(pattern, out):
33 | private.add(r.group(1))
34 |
35 | for r in re.finditer(pub_pattern, out):
36 | public.add(r.group(1))
37 |
38 | return private, public
--------------------------------------------------------------------------------
/.settings/org.eclipse.core.resources.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | encoding//api/api_helpers.py=utf-8
3 | encoding//api/api_utils.py=utf-8
4 | encoding//api/app_utils.py=utf-8
5 | encoding//app/__init__.py=utf-8
6 | encoding//app/dbs/__init__.py=utf-8
7 | encoding//app/others/tasks.py=utf-8
8 | encoding//app/utils/LogUtil.py=utf-8
9 | encoding//app/utils/OtherUtil.py=utf-8
10 | encoding//app/utils/PathUtil.py=utf-8
11 | encoding//app/utils/RequestUtil.py=utf-8
12 | encoding//app/utils/StringUtil.py=utf-8
13 | encoding//app/views/main_views.py=utf-8
14 | encoding//db/api_dbs.py=utf-8
15 | encoding//db/dsidx_dbs.py=utf-8
16 | encoding//db/mysql_utils.py=utf-8
17 | encoding//db/other_dbs.py=utf-8
18 | encoding//db/sqlite_utils.py=utf-8
19 | encoding//dump/class_dump_utils.py=utf-8
20 | encoding//dump/otool_utils.py=utf-8
21 | encoding//utils/report_utils.py=utf-8
22 | encoding//utils/utils.py=utf-8
23 | encoding/build_api_db.py=utf-8
24 | encoding/config.py=utf-8
25 | encoding/iOS_private.py=utf-8
26 | encoding/run_web.py=utf-8
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.gitignore.io/api/python
2 |
3 | ### Python ###
4 | # Byte-compiled / optimized / DLL files
5 | __pycache__/
6 | *.py[cod]
7 | *$py.class
8 |
9 | # C extensions
10 | *.so
11 |
12 | # Distribution / packaging
13 | .Python
14 | env/
15 | build/
16 | develop-eggs/
17 | dist/
18 | downloads/
19 | eggs/
20 | .eggs/
21 | lib64/
22 | parts/
23 | sdist/
24 | var/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | Ghtmlcov/
41 | .tox/
42 | .coverage
43 | .coverage.*
44 | .cache
45 | nosetests.xml
46 | coverage.xml
47 | *,cover
48 |
49 | # Translations
50 | *.mo
51 | *.pot
52 |
53 | # Django stuff:
54 | *.log
55 |
56 | # Sphinx documentation
57 | docs/_build/
58 |
59 | # PyBuilder
60 | target/
61 |
62 |
63 | # custom
64 | tmp/
65 | app/static/tmp/
66 | app/static/upload/*.ipa
67 | *.db
68 | docSet*
69 | utils/*.xlsx
70 | *.DS_Store
71 |
72 |
--------------------------------------------------------------------------------
/utils/utils.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年11月3日
4 |
5 | @author: atool
6 | '''
7 | import os, sys
8 | from config import class_dump_z_path
9 | import time
10 | import datetime
11 | import random
12 |
13 |
14 | def get_system():
15 | '''
16 | get system platform, to define use which class-dump-z
17 | '''
18 | system_platform = sys.platform
19 | if system_platform.startswith('linux'):
20 | return 'linux'
21 | elif system_platform.startswith('win32'):
22 | return 'win'
23 | elif system_platform.startswith('darwin'):
24 | return 'mac'
25 | else:
26 | return 'iphone'
27 |
28 |
29 | def get_clas_dump_path(use_what = 'class-dump'):
30 | '''
31 | get class-dump-z path
32 | '''
33 | if use_what == 'class-dump':
34 | cur_dir = os.getcwd()
35 | return os.path.join(cur_dir, 'class-dump')
36 | else:
37 | system = get_system()
38 | return class_dump_z_path.get(system, 'class-dump-z')
39 |
40 | def get_unique_str():
41 | #随机的名字,可以用于上传文件等等不重复,但有一定时间意义的名字
42 | datetime_str = time.strftime('%Y%m%d%H%M%S',time.localtime())
43 | return datetime_str + str(datetime.datetime.now().microsecond / 1000) + str(random.randint(0, 1000))
--------------------------------------------------------------------------------
/db/dsidx_dbs.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年10月27日
4 | .dsidx 文件解读
5 | @author: atool
6 | '''
7 | from db.sqlite_utils import SqliteHandler
8 | def get_dsidx_apis(db_path):
9 | '''
10 | has document apis
11 | info:获得带文档的api。(/Users/Test/Library/Developer/Shared/Documentation/DocSets/com.apple.adc.documentation.AppleiOS7.0.iOSLibrary.docset/Contents/Resources),在里面有个docSet.dsidx的文件,这就是Xcode针对api做的数据库,从这里可以获得带文档的api的各种信息了,从而有了带文档的api集合set_B。
12 |
13 | '''
14 | sql = "SELECT T.Z_PK, T.ZTOKENNAME, T.ZTOKENTYPE, T.ZCONTAINER, M.ZDECLAREDIN FROM ZTOKEN AS T, ZTOKENMETAINFORMATION AS M WHERE ZTOKENTYPE IN (3,9,12,13,16) AND T.Z_PK = M.ZTOKEN"
15 | apiset = SqliteHandler(db = db_path).exec_select(sql)
16 | return apiset
17 |
18 |
19 | def get_container_name(Z_PK, db_path):
20 | sql = "SELECT ZCONTAINERNAME FROM ZCONTAINER WHERE Z_PK = ?;"
21 | container = SqliteHandler(db = db_path).exec_select_one(sql, (Z_PK, ))
22 | if container:
23 | return container['ZCONTAINERNAME']
24 | return None
25 |
26 |
27 | def get_framework_and_header(Z_PK, db_path):
28 | sql = "SELECT ZFRAMEWORKNAME, ZHEADERPATH FROM ZHEADER WHERE Z_PK = ?;"
29 | rst = SqliteHandler(db = db_path).exec_select_one(sql, (Z_PK, ))
30 | return rst
31 |
--------------------------------------------------------------------------------
/utils/lib/xlsxwriter/compatibility.py:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | #
3 | # Python 2/3 compatibility functions for XlsxWriter.
4 | #
5 | # Copyright (c), 2013-2015, John McNamara, jmcnamara@cpan.org
6 | #
7 |
8 | import sys
9 | from decimal import Decimal
10 |
11 | try:
12 | # For compatibility between Python 2 and 3.
13 | from StringIO import StringIO
14 | except ImportError:
15 | from io import StringIO
16 |
17 | try:
18 | # For Python 2.6+.
19 | from fractions import Fraction
20 | except ImportError:
21 | Fraction = float
22 |
23 | try:
24 | # For Python 2.6+.
25 | from collections import defaultdict
26 | from collections import namedtuple
27 | except ImportError:
28 | # For Python 2.5 support.
29 | from .compat_collections import defaultdict
30 | from .compat_collections import namedtuple
31 |
32 | # Types to check in Python 2/3.
33 | if sys.version_info[0] == 2:
34 | num_types = (float, int, long, Decimal, Fraction)
35 | str_types = basestring
36 | else:
37 | num_types = (float, int, Decimal, Fraction)
38 | str_types = str
39 |
40 |
41 | if sys.version_info < (2, 6, 0):
42 | from StringIO import StringIO as BytesIO
43 | else:
44 | from io import BytesIO as BytesIO
45 |
--------------------------------------------------------------------------------
/config.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年10月27日
4 |
5 | @author: atool
6 | '''
7 | import os
8 |
9 | mysql_info = {
10 | 'HOST': '127.0.0.1',
11 | 'PORT': 3306,
12 | 'USERNAME': 'root',
13 | 'PASSWORD': 'root',
14 | 'CHARTSET': 'UTF8',
15 | 'DB': 'ios_private',
16 | }
17 |
18 | sqlite_info = {
19 | 'DB': 'ios_private.db',
20 | }
21 |
22 | #class_dump_z的路径
23 | cur_dir = os.getcwd()
24 |
25 | class_dump_z_path = {
26 | 'iphone': os.path.join(cur_dir, 'class_dump_z/iphone_armv6/class-dump-z'),
27 | 'linux': os.path.join(cur_dir, 'class_dump_z/linux_x86/class-dump-z'),
28 | 'mac': os.path.join(cur_dir, 'class_dump_z/mac_x86/class-dump-z'),
29 | 'win': os.path.join(cur_dir, 'class_dump_z/win_x86/class-dump-z')
30 | }
31 |
32 | #配置各个不同sdk版本的framework目录,
33 | sdks_config = []
34 |
35 | sdks_config.append({
36 | 'sdk': '8.1',
37 | 'framework': '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator8.1.sdk/System/Library/Frameworks/',
38 | 'private_framework': '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator8.1.sdk/System/Library/PrivateFrameworks/',
39 | 'docset': '/Applications/Xcode.app/Contents/Developer/Documentation/DocSets/com.apple.adc.documentation.AppleiOS8.1.iOSLibrary.docset/Contents/Resources/docSet.dsidx'
40 | })
--------------------------------------------------------------------------------
/app/wraps/mysql_escape_warp.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年3月30日
4 |
5 | @author: atool
6 | '''
7 |
8 | from functools import wraps
9 | import types
10 |
11 | import MySQLdb
12 |
13 | #对字典经典转义
14 | def _str_escape(s, d):
15 | if s == None:
16 | return ''
17 | return MySQLdb.escape_string(s)
18 |
19 | def _no_escape(s, d):
20 | if s == None:
21 | return ''
22 | return s
23 |
24 | def mysql_escape(f):
25 | @wraps(f)
26 | def decorated_function(*args, **kwargs):
27 | newargs = []
28 | #先转义参数,再执行方法
29 | for arg in args:
30 | #字符串,包括中文
31 | if type(arg) is types.StringType or type(arg) is types.UnicodeType:
32 | newargs.append(MySQLdb.escape_string(arg))
33 |
34 | #字典
35 | elif isinstance(arg, dict):
36 | newargs.append(MySQLdb.escape_dict(arg, {
37 | types.StringType: _str_escape,
38 | types.UnicodeType: _str_escape,
39 | types.IntType: _no_escape,
40 | types.FloatType: _no_escape
41 | }))
42 | #其他类型不转义
43 | else:
44 | newargs.append(arg)
45 |
46 | newargs = tuple(newargs)
47 |
48 | func = f(*newargs, **kwargs)
49 |
50 | return func
51 | return decorated_function
--------------------------------------------------------------------------------
/app/wraps/db_connect_warp.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年8月21日
4 | 数据库、redis连接装饰器,用于view上,给需要数据连接的加上相应的装饰器即可
5 | 保证在一次http连接过程中只需要进行一次mysql连接,减少内存占用,减少连接时间
6 | TODO 暂时不用
7 | @author: atool
8 | '''
9 |
10 | from functools import wraps
11 |
12 | import MySQLdb
13 | from flask import g
14 | from app import db_config
15 |
16 |
17 | def _connect_db():
18 | conn = MySQLdb.connect(host = db_config['DB_HOST'],
19 | user = db_config['DB_USER'],
20 | passwd = db_config['DB_PSW'],
21 | db = db_config['DB_NAME'],
22 | charset = db_config['DB_CHARSET'])
23 | #字典形式
24 | cursor = conn.cursor(cursorclass = MySQLdb.cursors.DictCursor)
25 | return (conn, cursor)
26 |
27 | #获取数据库连接装饰器
28 | def require_db_connection(f):
29 | @wraps(f)
30 | def decorated_function(*args, **kwargs):
31 | ####连接数据库
32 | if hasattr(g, 'conn') and g.conn != None and hasattr(g, 'cursor') and g.cursor != None:
33 | print 'has db connect, do nothing'
34 | else:
35 | (g.conn, g.cursor) = _connect_db()
36 | print 'create new db connect'
37 |
38 | #执行方法
39 | func = f(*args, **kwargs)
40 |
41 | ###关闭数据库连接
42 | if hasattr(g, 'conn') and g.conn != None and hasattr(g, 'cursor') and g.cursor != None:
43 | g.cursor.close()
44 | g.cursor = None
45 | g.conn.close()
46 | g.conn = None
47 | print 'close db connect'
48 | else:
49 | print 'no db connect, no need to close...'
50 |
51 | return func
52 | return decorated_function
53 |
54 |
--------------------------------------------------------------------------------
/app/static/res/js/ios_private.js:
--------------------------------------------------------------------------------
1 | Dropzone.autoDiscover = false;
2 | var myDropzone = new Dropzone("#ipa_file", {
3 | url: "/ipa_post",
4 | maxFilesize: 1024,
5 | acceptedFiles: '.ipa',
6 | maxFiles: 5,
7 | success: function(d, data) {
8 | data = JSON.parse(data);
9 | if (data.success == 1) {
10 | //显示app信息
11 | $('#app_name').text(data.data.name);
12 | $('#version').text(data.data.version);
13 | $('#bundle_identifier').text(data.data.bundle_id);
14 | $('#target_os_version').text(data.data.tar_version);
15 | $('#minimum_os_version').text(data.data.min_version);
16 | //显示ipa的架构信息
17 | $('#app_arcs').text(data.data.arcs.join(' / '));
18 | $('#minimum_os_version').text(data.data.min_version);
19 | $('#profile_type').text(data.data.profile_type);
20 | $('#expiration').text(data.data.expiration);
21 | //显示私有api信息
22 | $('#api_in_app div.api_section').remove();
23 | for (var i = 0; i < data.data.methods_in_app.length; i++) {
24 | var api = data.data.methods_in_app[i];
25 | var html = '
' +
26 | '
' + (i + 1) + '、' + api.api_name + '
' +
27 | 'api is ' + api.type + ', IN sdk ' + api.sdk + '、' + api.framework + ' -> ' + api.header_file + ' -> ' + api.class_name + ' -> '+ api.api_name +
28 | '';
29 | $('#api_append_div').append(html);
30 | };
31 | $('#framework_in_app div.api_section').remove();
32 | for (var i = 0; i < data.data.private_framework.length; i++) {
33 | var framework = data.data.private_framework[i];
34 | var html = '' +
35 | '
' + (i + 1) + '、' + framework + '
' +
36 | '';
37 | $('#framework_append_div').append(html);
38 | };
39 | }
40 | else {
41 | alert(data.data);
42 | }
43 | }
44 | });
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## iOS私有API检查工具 ##
2 |
3 | 私有API检查的原因在于:苹果在app提审的时候,会检查app使用私有api的情况,对于使用了私有api的app,不予通过,这个工具的目地就是在提审之前检查一下,提高通过率。
4 |
5 | ***PS:代码写的一般,主要是在做工具功能,欢迎PR。***
6 |
7 | ### 一、功能 ###
8 |
9 | 目前功能主要有以下:
10 |
11 | 1. 从ipa中提取一些基本信息,例如app名字,sdk版本,包名等,可以辅助QA日常工作。
12 | 2. ipa架构检查,可以看出是否支持64位架构,可以辅助AppStore提审。
13 | 3. ipa使用私有api情况,可以辅助AppStore提审。
14 | 4. ipa info和provision配置项的检查,获得授权设备udid(参考项目iOS-checkIPA)。
15 | 5. 获取签名信息。
16 | 6. 批量检查APP,并生成excel报告,截图见下方。
17 |
18 | ### 二、如何使用 ###
19 |
20 | #### 1. 构建私有api库 ####
21 |
22 | - db/dsidx_dbs.py文件为解析docSet.dsidx的库,请实现将docSet.dsidx内容导出到sqlite中。docSet.dsidx是xcode作为代码提示的数据库,表示是apple公开的公有api。
23 |
24 | - 修改config.py中sdks_config字典,增加各个version的sdk路径,然后运行build_api_db.py,会自动解析私有api,存存储到sqlite中。
25 |
26 | - (项目中的数据库内容是我编译sdk7.0的数据,可以直接用。)
27 |
28 |
29 | #### 2. 检查ipa私有api ####
30 |
31 | 运行方式有二,建议第二种web方式:
32 |
33 | 1. 修改iOS_private.py main方法中的ipa路径,运行即可。
34 |
35 | 2. 使用Web上传运行的方式,运行python run_web.py(请先配置flask运行环境),然后浏览器输入127.0.0.1:9527 将ipa拖入上传框等待即可看到检查结果。
36 |
37 | 3. 使用batch_check方法批量运行目录中的ipa,并生成excel报告。
38 |
39 | ### 三、Screenshot ###
40 |
41 | - 网页检查展示
42 |
43 | 
44 |
45 | - 批量检测生成excel报告概要
46 |
47 | 
48 |
49 | - excel报告详细页
50 |
51 | 
52 |
53 | ### 四、参考项目 ###
54 |
55 | - [iOS-private-api-scanner](https://github.com/mrmign/iOS-private-api-scanner)
56 | - [RuntimeBrowser](https://github.com/nst/RuntimeBrowser/tree/master/tools/ios_headers_history)
57 | - [XlsxWriter](https://github.com/jmcnamara/XlsxWriter)
58 | - [iOS-checkIPA](https://github.com/apperian/iOS-checkIPA)
59 | - [iOS-api-scan.md](https://github.com/mrmign/iOS-private-api-scanner/blob/master/iOS-api-scan.md)
60 |
61 |
62 | ### 五、Note ###
63 |
64 | 1. `私有的api = (class-dump Framework下的库生成的头文件中的api - (Framework下的头文件里的api = 有文档的api + 没有文档的api)) + PrivateFramework下的api`。
65 | 2. 私有api在公开的Framework及私有的PrivateFramework都有。
66 | 3. 请暂时暂mac上运行,linux上暂时没有找到合适的、代替otool的工具,求推荐^^!
67 |
68 |
69 | ## License
70 |
71 | This code is distributed under the terms and conditions of the GPL v2 license.
72 |
--------------------------------------------------------------------------------
/app/dbs/inc/Mongo.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年8月21日
4 | Mongo db 操作类
5 | TODO Test
6 | @author: atool
7 | '''
8 | from app import db_config
9 | import pymongo
10 |
11 |
12 | class Mongo():
13 |
14 | #类变量
15 | DESC = pymongo.DESCENDING
16 | ASC = pymongo.ASCENDING
17 |
18 | def __init__(self, host = db_config['DB_HOST'],
19 | port = db_config['DB_PORT'],
20 | user = db_config['DB_USER'],
21 | passwd = db_config['DB_PSW'],
22 | db = db_config['DB_NAME'],
23 | charset = db_config['DB_CHARSET']):
24 |
25 | self.connection = pymongo.Connection(host, port)
26 | self.db = self.connection[db]
27 | self.db.authenticate(user, passwd)
28 |
29 |
30 | def insert(self, table, params):
31 | return self.db[table].insert(params)
32 |
33 | def save(self, table, params):
34 | return self.db[table].save(params)
35 |
36 | def find(self, table, params = {}, sort = {}, skip = 0, limit = 25):
37 | if skip == -1:
38 | skip = 0
39 |
40 | if limit < 0:
41 | return self.db[table].find(params).sort(sort).skip(skip)
42 | return self.db[table].find(params).sort(sort).limit(limit).skip(skip)
43 |
44 | def count(self, table, params):
45 | return self.db[table].find(params).count()
46 |
47 | def find_one(self, table, params = {}):
48 | return self.db[table].find_one(params)
49 |
50 | def remove(self, table, params):
51 | return self.db[table].remove(params)
52 |
53 | def update(self, table, params):
54 | return self.db[table].update(params)
55 |
56 | #高级应用
57 | #1. 分页
58 | def get_page_count(self, table, params = {}, page_size = 25):
59 | if page_size < 0:
60 | page_size = 25
61 |
62 | total_cnt = self.count(table, params)
63 | return (total_cnt - 1) / 25 + 1
64 |
65 | def find_page(self, table, params = {}, sort = {}, page = 1, page_size = 25):
66 | page_count = self.get_page_count(table, params, page_size)
67 | limit = page_size
68 |
69 | if page <= 0:
70 | page = 1
71 |
72 | if page > page_count:
73 | page = page_count
74 |
75 | skip = (page - 1) * page_size
76 |
77 | return self.find(table, params, sort, skip, limit)
78 |
79 |
80 | #test
81 | if __name__ == '__main__':
82 | #TODO
83 | pass
--------------------------------------------------------------------------------
/db/other_dbs.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年10月27日
4 |
5 | @author: atool
6 | '''
7 | from db.sqlite_utils import SqliteHandler
8 | def create_some_table():
9 | #从public framework 中dump出来的所有api,其中包含部分私有api(sql7)
10 | sql1 = ("create table framework_dump_apis("
11 | "api_name varchar,"
12 | "class_name varchar,"
13 | "type varchar,"
14 | "header_file varchar,"
15 | "sdk varchar,"
16 | "framework varchar)")
17 |
18 | #从public framework .h文件中解析代码解析出来的api
19 | sql2 = ("create table framework_header_apis("
20 | "api_name varchar,"
21 | "class_name varchar,"
22 | "type varchar,"
23 | "header_file varchar,"
24 | "sdk varchar,"
25 | "framework varchar)")
26 |
27 | #有文档的pi
28 | sql3 = ("create table document_apis("
29 | "api_name varchar,"
30 | "class_name varchar,"
31 | "type varchar,"
32 | "header_file varchar,"
33 | "sdk varchar,"
34 | "framework varchar)")
35 |
36 | #sql2 - sql3
37 | sql4 = ("create table undocument_apis("
38 | "api_name varchar,"
39 | "class_name varchar,"
40 | "type varchar,"
41 | "header_file varchar,"
42 | "sdk varchar,"
43 | "framework varchar)")
44 |
45 | #包括sql6,sql7的所有内容
46 | sql5 = ("create table private_apis("
47 | "api_name varchar,"
48 | "class_name varchar,"
49 | "type varchar,"
50 | "header_file varchar,"
51 | "sdk varchar,"
52 | "framework varchar)")
53 |
54 | #private framework dump出来的api,全部为私有api
55 | sql6 = ("create table private_framework_dump_apis("
56 | "api_name varchar,"
57 | "class_name varchar,"
58 | "type varchar,"
59 | "header_file varchar,"
60 | "sdk varchar,"
61 | "framework varchar)")
62 |
63 | sql7 = ("create table framework_private_apis("
64 | "api_name varchar,"
65 | "class_name varchar,"
66 | "type varchar,"
67 | "header_file varchar,"
68 | "sdk varchar,"
69 | "framework varchar)")
70 |
71 | sql8 = ("create table whitelist("
72 | "api_name varchar,"
73 | "class_name varchar,"
74 | "type varchar,"
75 | "header_file varchar,"
76 | "sdk varchar,"
77 | "framework varchar)")
78 | SqliteHandler().exec_sql(sql1, ())
79 | SqliteHandler().exec_sql(sql2, ())
80 | SqliteHandler().exec_sql(sql3, ())
81 | SqliteHandler().exec_sql(sql4, ())
82 | SqliteHandler().exec_sql(sql5, ())
83 | SqliteHandler().exec_sql(sql6, ())
84 | SqliteHandler().exec_sql(sql7, ())
85 | SqliteHandler().exec_sql(sql8, ())
86 |
87 |
--------------------------------------------------------------------------------
/app/views/main_views.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年6月16日
4 |
5 | @author: atool
6 | '''
7 | from app import app
8 | from app.utils import StringUtil, PathUtil, OtherUtil
9 | import flask
10 | from flask.globals import request
11 | from werkzeug.utils import secure_filename
12 | import os, shutil
13 | import iOS_private
14 |
15 | @app.route('/', methods=['GET'])
16 | def index_page():
17 | return flask.render_template('main/index_page.html')
18 |
19 |
20 | allow_ext = ['ipa']
21 | #ipa上传
22 | @app.route('/ipa_post', methods=['POST'])
23 | def ipa_post():
24 | rst = {}
25 | pid = StringUtil.get_unique_str()
26 | ipa_path = None
27 | try:
28 | upload_file = request.files['file']
29 | fname = secure_filename(upload_file.filename)
30 | suffix_name = fname.split('.')[-1]
31 | #文件后缀名不对时,不做存储处理
32 | if not suffix_name in allow_ext:
33 | rst['success'] = 0
34 | rst['success'] = 'file ext is not allowed'
35 | else:
36 | #为图片名称添加时间戳,防止不同文件同名
37 | fname = pid + '.' + suffix_name
38 | ipa_path = os.path.join(PathUtil.upload_dir(), fname)
39 | upload_file.save(ipa_path)
40 | rst['success'] = 1
41 | rst['data'] = {}
42 | #获得ipa信息
43 | rsts = iOS_private.check_app_info_and_provision(ipa_path)
44 | for key in rsts.keys():
45 | rst['data'][key] = rsts[key]
46 | # ipa_parse = IpaParse.IpaParse(ipa_path)
47 | # rst['data']['version'] = ipa_parse.version()
48 | # rst['data']['bundle_identifier'] = ipa_parse.bundle_identifier()
49 | # rst['data']['target_os_version'] = ipa_parse.target_os_version()
50 | # rst['data']['minimum_os_version'] = ipa_parse.minimum_os_version()
51 | #检查ios私有api
52 | app = iOS_private.get_executable_path(ipa_path, pid)
53 | print 'app', app
54 | methods_in_app, methods_not_in_app, private = iOS_private.check_private_api(app, pid)
55 | rst['data']['methods_in_app'] = methods_in_app
56 | rst['data']['private_framework'] = list(private)
57 | #检查ipa 64支持情况
58 | arcs = iOS_private.check_architectures(app)
59 | rst['data']['arcs'] = arcs
60 |
61 | except Exception, e:
62 | print e
63 | rst['success'] = 0
64 | rst['data'] = '检查失败,也许上传的包并非真正的ipa,或者系统出现错误!'
65 |
66 | if ipa_path and os.path.exists(ipa_path):
67 | os.remove(ipa_path) #删除上传的包
68 |
69 | cur_dir = os.getcwd() #删除检查临时目录
70 | dest_tmp = os.path.join(cur_dir, 'tmp/' + pid)
71 | if os.path.exists(dest_tmp):
72 | shutil.rmtree(dest_tmp)
73 | #print rst
74 | return OtherUtil.object_2_dict(rst)
75 |
76 | #定义404页面
77 | @app.errorhandler(404)
78 | def page_not_found(error):
79 | return '404'
80 |
81 | @app.errorhandler(502)
82 | def server_502_error(error):
83 | return '502'
84 |
85 |
86 | @app.route('/not_allow', methods=['GET'])
87 | def deny(error):
88 | return 'You IP address is not in white list...'
--------------------------------------------------------------------------------
/utils/lib/xlsxwriter/chart_radar.py:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | #
3 | # ChartRadar - A class for writing the Excel XLSX Radar charts.
4 | #
5 | # Copyright 2013-2015, John McNamara, jmcnamara@cpan.org
6 | #
7 |
8 | from . import chart
9 |
10 |
11 | class ChartRadar(chart.Chart):
12 | """
13 | A class for writing the Excel XLSX Radar charts.
14 |
15 |
16 | """
17 |
18 | ###########################################################################
19 | #
20 | # Public API.
21 | #
22 | ###########################################################################
23 |
24 | def __init__(self, options=None):
25 | """
26 | Constructor.
27 |
28 | """
29 | super(ChartRadar, self).__init__()
30 |
31 | if options is None:
32 | options = {}
33 |
34 | self.subtype = options.get('subtype')
35 |
36 | if not self.subtype:
37 | self.subtype = 'marker'
38 | self.default_marker = {'type': 'none'}
39 |
40 | # Override and reset the default axis values.
41 | self.x_axis['defaults']['major_gridlines'] = {'visible': 1}
42 | self.set_x_axis({})
43 |
44 | # Set the available data label positions for this chart type.
45 | self.label_position_default = 'center'
46 | self.label_positions = {'center': 'ctr'}
47 |
48 | # Hardcode major_tick_mark for now until there is an accessor.
49 | self.y_axis['major_tick_mark'] = 'cross'
50 |
51 | ###########################################################################
52 | #
53 | # Private API.
54 | #
55 | ###########################################################################
56 |
57 | def _write_chart_type(self, args):
58 | # Write the c:radarChart element.
59 | self._write_radar_chart(args)
60 |
61 | ###########################################################################
62 | #
63 | # XML methods.
64 | #
65 | ###########################################################################
66 |
67 | def _write_radar_chart(self, args):
68 | # Write the element.
69 |
70 | if args['primary_axes']:
71 | series = self._get_primary_axes_series()
72 | else:
73 | series = self._get_secondary_axes_series()
74 |
75 | if not len(series):
76 | return
77 |
78 | self._xml_start_tag('c:radarChart')
79 |
80 | # Write the c:radarStyle element.
81 | self._write_radar_style()
82 |
83 | # Write the series elements.
84 | for data in series:
85 | self._write_ser(data)
86 |
87 | # Write the c:axId elements
88 | self._write_axis_ids(args)
89 |
90 | self._xml_end_tag('c:radarChart')
91 |
92 | def _write_radar_style(self):
93 | # Write the element.
94 | val = 'marker'
95 |
96 | if self.subtype == 'filled':
97 | val = 'filled'
98 |
99 | attributes = [('val', val)]
100 |
101 | self._xml_empty_tag('c:radarStyle', attributes)
102 |
--------------------------------------------------------------------------------
/utils/lib/xlsxwriter/chart_area.py:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | #
3 | # ChartArea - A class for writing the Excel XLSX Area charts.
4 | #
5 | # Copyright 2013-2015, John McNamara, jmcnamara@cpan.org
6 | #
7 |
8 | from . import chart
9 |
10 |
11 | class ChartArea(chart.Chart):
12 | """
13 | A class for writing the Excel XLSX Area charts.
14 |
15 |
16 | """
17 |
18 | ###########################################################################
19 | #
20 | # Public API.
21 | #
22 | ###########################################################################
23 |
24 | def __init__(self, options=None):
25 | """
26 | Constructor.
27 |
28 | """
29 | super(ChartArea, self).__init__()
30 |
31 | if options is None:
32 | options = {}
33 |
34 | self.subtype = options.get('subtype')
35 |
36 | if not self.subtype:
37 | self.subtype = 'standard'
38 |
39 | self.cross_between = 'midCat'
40 | self.show_crosses = 0
41 |
42 | # Override and reset the default axis values.
43 | if self.subtype == 'percent_stacked':
44 | self.y_axis['defaults']['num_format'] = '0%'
45 |
46 | # Set the available data label positions for this chart type.
47 | self.label_position_default = 'center'
48 | self.label_positions = {'center': 'ctr'}
49 |
50 | self.set_y_axis({})
51 |
52 | ###########################################################################
53 | #
54 | # Private API.
55 | #
56 | ###########################################################################
57 |
58 | def _write_chart_type(self, args):
59 | # Override the virtual superclass method with a chart specific method.
60 | # Write the c:areaChart element.
61 | self._write_area_chart(args)
62 |
63 | ###########################################################################
64 | #
65 | # XML methods.
66 | #
67 | ###########################################################################
68 | #
69 | def _write_area_chart(self, args):
70 | # Write the element.
71 |
72 | if args['primary_axes']:
73 | series = self._get_primary_axes_series()
74 | else:
75 | series = self._get_secondary_axes_series()
76 |
77 | if not len(series):
78 | return
79 |
80 | subtype = self.subtype
81 |
82 | if subtype == 'percent_stacked':
83 | subtype = 'percentStacked'
84 |
85 | self._xml_start_tag('c:areaChart')
86 |
87 | # Write the c:grouping element.
88 | self._write_grouping(subtype)
89 |
90 | # Write the series elements.
91 | for data in series:
92 | self._write_ser(data)
93 |
94 | # Write the c:dropLines element.
95 | self._write_drop_lines()
96 |
97 | # Write the c:marker element.
98 | self._write_marker_value()
99 |
100 | # Write the c:axId elements
101 | self._write_axis_ids(args)
102 |
103 | self._xml_end_tag('c:areaChart')
104 |
--------------------------------------------------------------------------------
/utils/lib/xlsxwriter/chart_doughnut.py:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | #
3 | # ChartDoughnut - A class for writing the Excel XLSX Doughnut charts.
4 | #
5 | # Copyright 2013-2015, John McNamara, jmcnamara@cpan.org
6 | #
7 |
8 | from warnings import warn
9 | from . import chart_pie
10 |
11 |
12 | class ChartDoughnut(chart_pie.ChartPie):
13 | """
14 | A class for writing the Excel XLSX Doughnut charts.
15 |
16 |
17 | """
18 |
19 | ###########################################################################
20 | #
21 | # Public API.
22 | #
23 | ###########################################################################
24 |
25 | def __init__(self, options=None):
26 | """
27 | Constructor.
28 |
29 | """
30 | super(ChartDoughnut, self).__init__()
31 |
32 | if options is None:
33 | options = {}
34 |
35 | self.vary_data_color = 1
36 | self.rotation = 0
37 | self.hole_size = 50
38 |
39 | def set_hole_size(self, size):
40 | """
41 | Set the Doughnut chart hole size.
42 |
43 | Args:
44 | size: 10 <= size <= 90.
45 |
46 | Returns:
47 | Nothing.
48 |
49 | """
50 | if size is None:
51 | return
52 |
53 | # Ensure the size is in Excel's range.
54 | if size < 10 or size > 90:
55 | warn("Chart hole size %d outside Excel range: 10 <= size <= 90"
56 | % size)
57 | return
58 |
59 | self.hole_size = int(size)
60 |
61 | ###########################################################################
62 | #
63 | # Private API.
64 | #
65 | ###########################################################################
66 |
67 | def _write_chart_type(self, args):
68 | # Override the virtual superclass method with a chart specific method.
69 | # Write the c:doughnutChart element.
70 | self._write_doughnut_chart(args)
71 |
72 | ###########################################################################
73 | #
74 | # XML methods.
75 | #
76 | ###########################################################################
77 |
78 | def _write_doughnut_chart(self, args):
79 | # Write the element. Over-ridden method to remove
80 | # axis_id code since Doughnut charts don't require val and cat axes.
81 | self._xml_start_tag('c:doughnutChart')
82 |
83 | # Write the c:varyColors element.
84 | self._write_vary_colors()
85 |
86 | # Write the series elements.
87 | for data in self.series:
88 | self._write_ser(data)
89 |
90 | # Write the c:firstSliceAng element.
91 | self._write_first_slice_ang()
92 |
93 | # Write the c:holeSize element.
94 | self._write_c_hole_size()
95 |
96 | self._xml_end_tag('c:doughnutChart')
97 |
98 | def _write_c_hole_size(self):
99 | # Write the element.
100 | attributes = [('val', self.hole_size)]
101 |
102 | self._xml_empty_tag('c:holeSize', attributes)
103 |
--------------------------------------------------------------------------------
/db/api_dbs.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年10月27日
4 |
5 | @author: atool
6 | '''
7 | from db.sqlite_utils import SqliteHandler
8 |
9 | #批量插入有文档的api
10 | def insert_apis(table_name, datas):
11 | sql = "insert into " + table_name + "(api_name, class_name, type, header_file, sdk, framework) values(:api_name, :class_name, :type, :header_file, :sdk, :framework)"
12 | return SqliteHandler().exec_insert_many(sql, datas)
13 |
14 |
15 | def delete_apis_by_sdk(table_name, sdk):
16 | sql = "delete from " + table_name + " where sdk = ?;"
17 | return SqliteHandler().exec_update(sql, (sdk, ))
18 |
19 | # private_apis = []
20 |
21 | # def get_private_api_list():
22 | # '''
23 | # 缓存数据,批量检查的时候,减少sql io
24 | # '''
25 | # global private_apis
26 | # if not private_apis:
27 | # sql = "select * from private_apis group by api_name having private_apis.api_name not in (select api_name from whitelist group by api_name);"
28 | # params = ()
29 | # private_apis = SqliteHandler().exec_select(sql, params)
30 |
31 | # return private_apis
32 |
33 | def get_private_api_list(framework = None):
34 | framework_str = None
35 | if framework and len(framework) > 0:
36 | framework_str = '('
37 | for f in framework:
38 | framework_str = framework_str + "'" + f + "', "
39 |
40 | framework_str = framework_str[0:-2]
41 | framework_str = framework_str + ')'
42 |
43 | #有frame过滤条件
44 | if framework_str:
45 | sql = "select * from private_apis group by api_name having private_apis.framework in " + framework_str + " and private_apis.api_name not in (select api_name from whitelist group by api_name);"
46 | params = ()
47 | else:
48 | sql = "select * from private_apis group by api_name having private_apis.api_name not in (select api_name from whitelist group by api_name);"
49 | params = ()
50 | private_apis = SqliteHandler().exec_select(sql, params)
51 | return private_apis
52 |
53 | white_apis = []
54 | def get_white_api_list():
55 | '''
56 | 白名单中的api
57 | 缓存数据,批量检查的时候,减少sql io
58 | '''
59 | global white_apis
60 | if not white_apis:
61 | sql = "select * from whitelist group by api_name;"
62 | params = ()
63 | private_apis = SqliteHandler().exec_select(sql, params)
64 |
65 | return private_apis
66 |
67 | #获得所有的私有框架dump出来的api
68 | def get_private_framework_dump_apis(sdk):
69 | sql = "select * from private_framework_dump_apis where sdk = ?"
70 | params = (sdk, )
71 | return SqliteHandler().exec_select(sql, params)
72 |
73 | #获得所有的共有框架dump出来的api
74 | def get_framework_dump_apis(sdk):
75 | sql = "select * from framework_dump_apis where sdk = ?"
76 | params = (sdk, )
77 | return SqliteHandler().exec_select(sql, params)
78 |
79 | def get_framework_private_apis():
80 | sql = "select * from framework_private_apis group by api_name;"
81 | params = ()
82 | return SqliteHandler().exec_select(sql, params)
83 |
84 |
85 | def is_api_exist_in(table_name, api):
86 | sql = "select * from " + table_name + " where api_name = ? and class_name = ? and sdk = ?;"
87 | params = (api['api_name'], api['class_name'], api['sdk'])
88 | return SqliteHandler().exec_select_one(sql, params)
--------------------------------------------------------------------------------
/db/sqlite_utils.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年10月27日
4 | 数据库操作类
5 | @author: atool
6 | '''
7 | import sqlite3
8 | from config import sqlite_info
9 |
10 | def dict_factory(cursor, row):
11 | d = {}
12 | for idx, col in enumerate(cursor.description):
13 | d[col[0]] = row[idx]
14 | return d
15 |
16 |
17 | class SqliteHandler():
18 | #对象属性
19 | #连接
20 | conn = None
21 | #数据游标
22 | cursor = None
23 |
24 | #构造函数
25 | def __init__(self, db = sqlite_info['DB']):
26 | self.db = db
27 | self.__connect()
28 |
29 | def __connect(self):
30 | try:
31 | self.conn = sqlite3.connect(self.db)
32 | self.conn.row_factory = dict_factory
33 | self.cursor = self.conn.cursor()
34 | except Exception, e:
35 | print e
36 |
37 | def close(self):
38 | try:
39 | self.cursor.close()
40 | self.conn.close()
41 | except:
42 | pass
43 |
44 | def exec_select(self, sql, params = ()):
45 | '''
46 | ps:执行查询类型的sql语句
47 | '''
48 | try:
49 | self.cursor.execute(sql, params)
50 | result_set = self.cursor.fetchall()
51 | return result_set
52 | except Exception, e:
53 | print e
54 | return False
55 |
56 | def exec_select_one(self, sql, params = ()):
57 | '''
58 | ps:执行查询类型的sql语句
59 | '''
60 | try:
61 | self.cursor.execute(sql, params)
62 | result_set = self.cursor.fetchone()
63 | return result_set
64 | except Exception, e:
65 | print e
66 | return False
67 |
68 | def exec_insert(self, sql, params = ()):
69 | '''
70 | ps:执行插入类sql语句
71 | '''
72 | try:
73 | # 执行sql语句
74 | self.cursor.execute(sql, params)
75 | # 提交到数据库执行
76 | insert_id = self.cursor.lastrowid
77 | self.conn.commit()
78 | return insert_id
79 | except Exception as e:
80 | print e
81 | self.conn.rollback()
82 | return False
83 |
84 | def exec_insert_many(self, sql, datas):
85 | try:
86 | # 执行sql语句
87 | self.cursor.executemany(sql, datas)
88 | # 提交到数据库执行
89 | row_count = self.cursor.rowcount
90 | self.conn.commit()
91 | return row_count
92 | except Exception, e:
93 | print e
94 | self.conn.rollback()
95 | return False
96 |
97 | def exec_update(self, sql, params = ()):
98 | '''
99 | ps:执行更新类sql语句
100 | '''
101 | try:
102 | # 执行sql语句
103 | self.cursor.execute(sql, params)
104 | row_count = self.cursor.rowcount
105 | # 提交到数据库执行
106 | self.conn.commit()
107 | if row_count == False:
108 | row_count = True
109 | return row_count
110 | except Exception, e:
111 | print e
112 | self.conn.rollback()
113 | return False
114 |
115 | def exec_sql(self, sql, params = ()):
116 | try:
117 | # 执行sql语句
118 | self.cursor.execute(sql, params)
119 | # 提交到数据库执行
120 | self.conn.commit()
121 | return True
122 | except Exception, e:
123 | print e
124 | self.conn.rollback()
125 | return False
--------------------------------------------------------------------------------
/utils/lib/xlsxwriter/chart_line.py:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | #
3 | # ChartLine - A class for writing the Excel XLSX Line charts.
4 | #
5 | # Copyright 2013-2015, John McNamara, jmcnamara@cpan.org
6 | #
7 |
8 | from . import chart
9 |
10 |
11 | class ChartLine(chart.Chart):
12 | """
13 | A class for writing the Excel XLSX Line charts.
14 |
15 |
16 | """
17 |
18 | ###########################################################################
19 | #
20 | # Public API.
21 | #
22 | ###########################################################################
23 |
24 | def __init__(self, options=None):
25 | """
26 | Constructor.
27 |
28 | """
29 | super(ChartLine, self).__init__()
30 |
31 | if options is None:
32 | options = {}
33 |
34 | self.default_marker = {'type': 'none'}
35 | self.smooth_allowed = True
36 |
37 | # Set the available data label positions for this chart type.
38 | self.label_position_default = 'right'
39 | self.label_positions = {
40 | 'center': 'ctr',
41 | 'right': 'r',
42 | 'left': 'l',
43 | 'above': 't',
44 | 'below': 'b',
45 | # For backward compatibility.
46 | 'top': 't',
47 | 'bottom': 'b'}
48 |
49 | ###########################################################################
50 | #
51 | # Private API.
52 | #
53 | ###########################################################################
54 |
55 | def _write_chart_type(self, args):
56 | # Override the virtual superclass method with a chart specific method.
57 | # Write the c:lineChart element.
58 | self._write_line_chart(args)
59 |
60 | ###########################################################################
61 | #
62 | # XML methods.
63 | #
64 | ###########################################################################
65 |
66 | def _write_line_chart(self, args):
67 | # Write the element.
68 |
69 | if args['primary_axes']:
70 | series = self._get_primary_axes_series()
71 | else:
72 | series = self._get_secondary_axes_series()
73 |
74 | if not len(series):
75 | return
76 |
77 | self._xml_start_tag('c:lineChart')
78 |
79 | # Write the c:grouping element.
80 | self._write_grouping('standard')
81 |
82 | # Write the series elements.
83 | for data in series:
84 | self._write_ser(data)
85 |
86 | # Write the c:dropLines element.
87 | self._write_drop_lines()
88 |
89 | # Write the c:hiLowLines element.
90 | self._write_hi_low_lines()
91 |
92 | # Write the c:upDownBars element.
93 | self._write_up_down_bars()
94 |
95 | # Write the c:marker element.
96 | self._write_marker_value()
97 |
98 | # Write the c:axId elements
99 | self._write_axis_ids(args)
100 |
101 | self._xml_end_tag('c:lineChart')
102 |
103 | def _write_d_pt_point(self, index, point):
104 | # Write an individual element. Override the parent method to
105 | # add markers.
106 |
107 | self._xml_start_tag('c:dPt')
108 |
109 | # Write the c:idx element.
110 | self._write_idx(index)
111 |
112 | self._xml_start_tag('c:marker')
113 |
114 | # Write the c:spPr element.
115 | self._write_sp_pr(point)
116 |
117 | self._xml_end_tag('c:marker')
118 |
119 | self._xml_end_tag('c:dPt')
120 |
--------------------------------------------------------------------------------
/utils/lib/xlsxwriter/relationships.py:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | #
3 | # Relationships - A class for writing the Excel XLSX Worksheet file.
4 | #
5 | # Copyright 2013-2015, John McNamara, jmcnamara@cpan.org
6 | #
7 |
8 | # Package imports.
9 | from . import xmlwriter
10 |
11 | # Long namespace strings used in the class.
12 | schema_root = 'http://schemas.openxmlformats.org'
13 | package_schema = schema_root + '/package/2006/relationships'
14 | document_schema = schema_root + '/officeDocument/2006/relationships'
15 |
16 |
17 | class Relationships(xmlwriter.XMLwriter):
18 | """
19 | A class for writing the Excel XLSX Relationships file.
20 |
21 |
22 | """
23 |
24 | ###########################################################################
25 | #
26 | # Public API.
27 | #
28 | ###########################################################################
29 |
30 | def __init__(self):
31 | """
32 | Constructor.
33 |
34 | """
35 |
36 | super(Relationships, self).__init__()
37 |
38 | self.relationships = []
39 | self.id = 1
40 |
41 | ###########################################################################
42 | #
43 | # Private API.
44 | #
45 | ###########################################################################
46 |
47 | def _assemble_xml_file(self):
48 | # Assemble and write the XML file.
49 |
50 | # Write the XML declaration.
51 | self._xml_declaration()
52 |
53 | self._write_relationships()
54 |
55 | # Close the file.
56 | self._xml_close()
57 |
58 | def _add_document_relationship(self, rel_type, target, target_mode=None):
59 | # Add container relationship to XLSX .rels xml files.
60 | rel_type = document_schema + rel_type
61 |
62 | self.relationships.append((rel_type, target, target_mode))
63 |
64 | def _add_package_relationship(self, rel_type, target):
65 | # Add container relationship to XLSX .rels xml files.
66 | rel_type = package_schema + rel_type
67 |
68 | self.relationships.append((rel_type, target, None))
69 |
70 | def _add_ms_package_relationship(self, rel_type, target):
71 | # Add container relationship to XLSX .rels xml files. Uses MS schema.
72 | schema = 'http://schemas.microsoft.com/office/2006/relationships'
73 | rel_type = schema + rel_type
74 |
75 | self.relationships.append((rel_type, target, None))
76 |
77 | def _add_worksheet_relationship(self, rel_type, target, target_mode=None):
78 | # Add worksheet relationship to sheet.rels xml files.
79 | rel_type = document_schema + rel_type
80 |
81 | self.relationships.append((rel_type, target, target_mode))
82 |
83 | ###########################################################################
84 | #
85 | # XML methods.
86 | #
87 | ###########################################################################
88 |
89 | def _write_relationships(self):
90 | # Write the element.
91 | attributes = [('xmlns', package_schema,)]
92 |
93 | self._xml_start_tag('Relationships', attributes)
94 |
95 | for relationship in self.relationships:
96 | self._write_relationship(relationship)
97 |
98 | self._xml_end_tag('Relationships')
99 |
100 | def _write_relationship(self, relationship):
101 | # Write the element.
102 | rel_type, target, target_mode = relationship
103 |
104 | attributes = [
105 | ('Id', 'rId' + str(self.id)),
106 | ('Type', rel_type),
107 | ('Target', target),
108 | ]
109 |
110 | self.id += 1
111 |
112 | if target_mode:
113 | attributes.append(('TargetMode', target_mode))
114 |
115 | self._xml_empty_tag('Relationship', attributes)
116 |
--------------------------------------------------------------------------------
/utils/lib/xlsxwriter/chart_column.py:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | #
3 | # ChartColumn - A class for writing the Excel XLSX Column charts.
4 | #
5 | # Copyright 2013-2015, John McNamara, jmcnamara@cpan.org
6 | #
7 |
8 | from . import chart
9 |
10 |
11 | class ChartColumn(chart.Chart):
12 | """
13 | A class for writing the Excel XLSX Column charts.
14 |
15 |
16 | """
17 |
18 | ###########################################################################
19 | #
20 | # Public API.
21 | #
22 | ###########################################################################
23 |
24 | def __init__(self, options=None):
25 | """
26 | Constructor.
27 |
28 | """
29 | super(ChartColumn, self).__init__()
30 |
31 | if options is None:
32 | options = {}
33 |
34 | self.subtype = options.get('subtype')
35 |
36 | if not self.subtype:
37 | self.subtype = 'clustered'
38 |
39 | self.horiz_val_axis = 0
40 |
41 | if self.subtype == 'percent_stacked':
42 | self.y_axis['defaults']['num_format'] = '0%'
43 |
44 | # Set the available data label positions for this chart type.
45 | self.label_position_default = 'outside_end'
46 | self.label_positions = {
47 | 'center': 'ctr',
48 | 'inside_base': 'inBase',
49 | 'inside_end': 'inEnd',
50 | 'outside_end': 'outEnd'}
51 |
52 | self.set_y_axis({})
53 |
54 | ###########################################################################
55 | #
56 | # Private API.
57 | #
58 | ###########################################################################
59 |
60 | def _write_chart_type(self, args):
61 | # Override the virtual superclass method with a chart specific method.
62 |
63 | # Write the c:barChart element.
64 | self._write_bar_chart(args)
65 |
66 | def _write_bar_chart(self, args):
67 | # Write the element.
68 |
69 | if args['primary_axes']:
70 | series = self._get_primary_axes_series()
71 | else:
72 | series = self._get_secondary_axes_series()
73 |
74 | if not len(series):
75 | return
76 |
77 | subtype = self.subtype
78 | if subtype == 'percent_stacked':
79 | subtype = 'percentStacked'
80 |
81 | # Set a default overlap for stacked charts.
82 | if 'stacked' in self.subtype:
83 | if self.series_overlap_1 is None:
84 | self.series_overlap_1 = 100
85 |
86 | self._xml_start_tag('c:barChart')
87 |
88 | # Write the c:barDir element.
89 | self._write_bar_dir()
90 |
91 | # Write the c:grouping element.
92 | self._write_grouping(subtype)
93 |
94 | # Write the c:ser elements.
95 | for data in series:
96 | self._write_ser(data)
97 |
98 | # Write the c:marker element.
99 | self._write_marker_value()
100 |
101 | # Write the c:gapWidth element.
102 | if args['primary_axes']:
103 | self._write_gap_width(self.series_gap_1)
104 | else:
105 | self._write_gap_width(self.series_gap_2)
106 |
107 | # Write the c:overlap element.
108 | if args['primary_axes']:
109 | self._write_overlap(self.series_overlap_1)
110 | else:
111 | self._write_overlap(self.series_overlap_2)
112 |
113 | # Write the c:axId elements
114 | self._write_axis_ids(args)
115 |
116 | self._xml_end_tag('c:barChart')
117 |
118 | ###########################################################################
119 | #
120 | # XML methods.
121 | #
122 | ###########################################################################
123 |
124 | def _write_bar_dir(self):
125 | # Write the element.
126 | val = 'col'
127 |
128 | attributes = [('val', val)]
129 |
130 | self._xml_empty_tag('c:barDir', attributes)
131 |
132 | def _write_err_dir(self, val):
133 | # Overridden from Chart class since it is not used in Column charts.
134 | pass
135 |
--------------------------------------------------------------------------------
/utils/lib/xlsxwriter/chart_stock.py:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | #
3 | # ChartStock - A class for writing the Excel XLSX Stock charts.
4 | #
5 | # Copyright 2013-2015, John McNamara, jmcnamara@cpan.org
6 | #
7 |
8 | from . import chart
9 |
10 |
11 | class ChartStock(chart.Chart):
12 | """
13 | A class for writing the Excel XLSX Stock charts.
14 |
15 | """
16 |
17 | ###########################################################################
18 | #
19 | # Public API.
20 | #
21 | ###########################################################################
22 |
23 | def __init__(self, options=None):
24 | """
25 | Constructor.
26 |
27 | """
28 | super(ChartStock, self).__init__()
29 |
30 | if options is None:
31 | options = {}
32 |
33 | self.show_crosses = 0
34 | self.hi_low_lines = {}
35 | self.date_category = True
36 |
37 | # Override and reset the default axis values.
38 | self.x_axis['defaults']['num_format'] = 'dd/mm/yyyy'
39 | self.x2_axis['defaults']['num_format'] = 'dd/mm/yyyy'
40 |
41 | # Set the available data label positions for this chart type.
42 | self.label_position_default = 'right'
43 | self.label_positions = {
44 | 'center': 'ctr',
45 | 'right': 'r',
46 | 'left': 'l',
47 | 'above': 't',
48 | 'below': 'b',
49 | # For backward compatibility.
50 | 'top': 't',
51 | 'bottom': 'b'}
52 |
53 | self.set_x_axis({})
54 | self.set_x2_axis({})
55 |
56 | ###########################################################################
57 | #
58 | # Private API.
59 | #
60 | ###########################################################################
61 |
62 | def _write_chart_type(self, args):
63 | # Override the virtual superclass method with a chart specific method.
64 | # Write the c:stockChart element.
65 | self._write_stock_chart(args)
66 |
67 | ###########################################################################
68 | #
69 | # XML methods.
70 | #
71 | ###########################################################################
72 |
73 | def _write_stock_chart(self, args):
74 | # Write the element.
75 | # Overridden to add hi_low_lines().
76 |
77 | if args['primary_axes']:
78 | series = self._get_primary_axes_series()
79 | else:
80 | series = self._get_secondary_axes_series()
81 |
82 | if not len(series):
83 | return
84 |
85 | # Add default formatting to the series data.
86 | self._modify_series_formatting()
87 |
88 | self._xml_start_tag('c:stockChart')
89 |
90 | # Write the series elements.
91 | for data in series:
92 | self._write_ser(data)
93 |
94 | # Write the c:dropLines element.
95 | self._write_drop_lines()
96 |
97 | # Write the c:hiLowLines element.
98 | if args.get('primary_axes'):
99 | self._write_hi_low_lines()
100 |
101 | # Write the c:upDownBars element.
102 | self._write_up_down_bars()
103 |
104 | # Write the c:marker element.
105 | self._write_marker_value()
106 |
107 | # Write the c:axId elements
108 | self._write_axis_ids(args)
109 |
110 | self._xml_end_tag('c:stockChart')
111 |
112 | def _modify_series_formatting(self):
113 | # Add default formatting to the series data.
114 |
115 | index = 0
116 |
117 | for series in self.series:
118 | if index % 4 != 3:
119 | if not series['line']['defined']:
120 | series['line'] = {'width': 2.25,
121 | 'none': 1,
122 | 'defined': 1}
123 |
124 | if series['marker'] is None:
125 | if index % 4 == 2:
126 | series['marker'] = {'type': 'dot', 'size': 3}
127 | else:
128 | series['marker'] = {'type': 'none'}
129 |
130 | index += 1
131 |
--------------------------------------------------------------------------------
/utils/lib/xlsxwriter/sharedstrings.py:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | #
3 | # SharedStrings - A class for writing the Excel XLSX sharedStrings file.
4 | #
5 | # Copyright 2013-2015, John McNamara, jmcnamara@cpan.org
6 | #
7 |
8 | # Standard packages.
9 | import re
10 |
11 | # Package imports.
12 | from . import xmlwriter
13 |
14 |
15 | class SharedStrings(xmlwriter.XMLwriter):
16 | """
17 | A class for writing the Excel XLSX sharedStrings file.
18 |
19 | """
20 |
21 | ###########################################################################
22 | #
23 | # Public API.
24 | #
25 | ###########################################################################
26 |
27 | def __init__(self):
28 | """
29 | Constructor.
30 |
31 | """
32 |
33 | super(SharedStrings, self).__init__()
34 |
35 | self.string_table = None
36 |
37 | ###########################################################################
38 | #
39 | # Private API.
40 | #
41 | ###########################################################################
42 |
43 | def _assemble_xml_file(self):
44 | # Assemble and write the XML file.
45 |
46 | # Write the XML declaration.
47 | self._xml_declaration()
48 |
49 | # Write the sst element.
50 | self._write_sst()
51 |
52 | # Write the sst strings.
53 | self._write_sst_strings()
54 |
55 | # Close the sst tag.
56 | self._xml_end_tag('sst')
57 |
58 | # Close the file.
59 | self._xml_close()
60 |
61 | ###########################################################################
62 | #
63 | # XML methods.
64 | #
65 | ###########################################################################
66 |
67 | def _write_sst(self):
68 | # Write the element.
69 | xmlns = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'
70 |
71 | attributes = [
72 | ('xmlns', xmlns),
73 | ('count', self.string_table.count),
74 | ('uniqueCount', self.string_table.unique_count),
75 | ]
76 |
77 | self._xml_start_tag('sst', attributes)
78 |
79 | def _write_sst_strings(self):
80 | # Write the sst string elements.
81 |
82 | for string in (self.string_table._get_strings()):
83 | self._write_si(string)
84 |
85 | def _write_si(self, string):
86 | # Write the element.
87 | attributes = []
88 |
89 | # Excel escapes control characters with _xHHHH_ and also escapes any
90 | # literal strings of that type by encoding the leading underscore.
91 | # So "\0" -> _x0000_ and "_x0000_" -> _x005F_x0000_.
92 | # The following substitutions deal with those cases.
93 |
94 | # Escape the escape.
95 | string = re.sub('(_x[0-9a-fA-F]{4}_)', r'_x005F\1', string)
96 |
97 | # Convert control character to the _xHHHH_ escape.
98 | string = re.sub(r'([\x00-\x08\x0B-\x1F])',
99 | lambda match: "_x%04X_" %
100 | ord(match.group(1)), string)
101 |
102 | # Add attribute to preserve leading or trailing whitespace.
103 | if re.search('^\s', string) or re.search('\s$', string):
104 | attributes.append(('xml:space', 'preserve'))
105 |
106 | # Write any rich strings without further tags.
107 | if re.search('^', string) and re.search('$', string):
108 | self._xml_rich_si_element(string)
109 | else:
110 | self._xml_si_element(string, attributes)
111 |
112 |
113 | # A metadata class to store Excel strings between worksheets.
114 | class SharedStringTable(object):
115 | """
116 | A class to track Excel shared strings between worksheets.
117 |
118 | """
119 |
120 | def __init__(self):
121 | self.count = 0
122 | self.unique_count = 0
123 | self.string_table = {}
124 | self.string_array = []
125 |
126 | def _get_shared_string_index(self, string):
127 | """" Get the index of the string in the Shared String table. """
128 | if string not in self.string_table:
129 | # String isn't already stored in the table so add it.
130 | index = self.unique_count
131 | self.string_table[string] = index
132 | self.count += 1
133 | self.unique_count += 1
134 | return index
135 | else:
136 | # String exists in the table.
137 | index = self.string_table[string]
138 | self.count += 1
139 | return index
140 |
141 | def _get_shared_string(self, index):
142 | """" Get a shared string from the index. """
143 | return self.string_array[index]
144 |
145 | def _sort_string_data(self):
146 | """" Sort the shared string data and convert from dict to list. """
147 | self.string_array = sorted(self.string_table,
148 | key=self.string_table.__getitem__)
149 | self.string_table = {}
150 |
151 | def _get_strings(self):
152 | """" Return the sorted string list. """
153 | return self.string_array
154 |
--------------------------------------------------------------------------------
/app/dbs/inc/Redis.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年8月21日
4 | Redis操作的库
5 | @author: atool
6 | '''
7 | #数据库配置
8 | import redis
9 | from app import redis_config
10 | import time
11 | from app.utils import OtherUtil
12 | from app.dbs.inc.Mysql import Mysql
13 |
14 | class RedisMysqlCache(object):
15 | '''
16 | a redis cache for mysql
17 | '''
18 | def __init__(self, timeout = 60 * 60, # one hour
19 | host = redis_config['RD_HOST'],
20 | port = redis_config['RD_PORT'],
21 | password = redis_config['RD_PSW'],
22 | db = redis_config['TEMP_DB'],
23 | charset = redis_config['RD_CHARSET']):
24 | self.__db = redis.Redis(host = host, port = port, password = password, db = db, charset = charset)
25 | self.timeout = timeout
26 |
27 | def __cal_key(self, sql, params, t = "select"):
28 | key = sql + t
29 | for p in params:
30 | key = key + str(p)
31 |
32 | key = OtherUtil.md5(key)
33 | return key
34 |
35 | def select_one(self, sql, params):
36 | '''
37 | ps:从redis中获取数据,如果数据不存在,则从数据库取出来放到redis中
38 | '''
39 | key = self.__cal_key(sql, params, t = "select_one")
40 | value = self.__db.get(key)
41 |
42 | expire = True
43 | try:
44 | value = eval(value)
45 | if time.time() - value['timestamp'] <= self.timeout and value['data']:
46 | #还没有过期
47 | expire = False
48 | rst = value['data']
49 | except:
50 | expire = True
51 |
52 | if expire:
53 | #cache过期,则重新从数据库加载
54 | rst = Mysql().exec_select_one(sql, params)
55 | if rst:
56 | #查询到结果,则缓存到redis
57 | value = {'timestamp': time.time(), 'data': rst}
58 | self.__db.set(key, value)
59 |
60 | return rst
61 |
62 | def select(self, sql, params):
63 | '''
64 | ps:从redis中获取数据,如果数据不存在,则从数据库取出来放到redis中
65 | '''
66 | key = self.__cal_key(sql, params)
67 |
68 | value = self.__db.get(key)
69 |
70 | expire = True
71 | try:
72 | value = eval(value)
73 | if time.time() - value['timestamp'] <= self.timeout:
74 | #还没有过期
75 | expire = False
76 | rst = value['data']
77 | except:
78 | expire = True
79 |
80 | if expire:
81 | #cache过期,则重新从数据库加载
82 | rst = Mysql().exec_select(sql, params)
83 | if rst:
84 | #查询到结果,则缓存到redis
85 | value = {'timestamp': time.time(), 'data': rst}
86 | self.__db.set(key, value)
87 |
88 | return rst
89 |
90 | class RedisQueue(object):
91 | """Simple Queue with Redis Backend"""
92 | def __init__(self, name,
93 | namespace = 'queue',
94 | host = redis_config['RD_HOST'],
95 | port = redis_config['RD_PORT'],
96 | password = redis_config['RD_PSW'],
97 | db = redis_config['TEST_DB'],
98 | charset = redis_config['RD_CHARSET']):
99 |
100 | """The default connection parameters are: host='localhost', port=6379, db=0"""
101 | self.__db = redis.Redis(host = host, port = port, password = password, db = db, charset = charset)
102 | self.key = '%s:%s' %(namespace, name)
103 |
104 | def qsize(self):
105 | """Return the approximate size of the queue."""
106 | return self.__db.llen(self.key)
107 |
108 | def empty(self):
109 | """Return True if the queue is empty, False otherwise."""
110 | return self.qsize() == 0
111 |
112 | def put(self, item):
113 | """Put item into the queue."""
114 | return self.__db.rpush(self.key, item)
115 |
116 | def get(self, block = True, timeout = None):
117 | """Remove and return an item from the queue.
118 |
119 | If optional args block is true and timeout is None (the default), block
120 | if necessary until an item is available."""
121 | if block:
122 | item = self.__db.blpop(self.key, timeout = timeout)
123 | else:
124 | item = self.__db.lpop(self.key)
125 |
126 | if item:
127 | item = item[1]
128 | return item
129 |
130 | #TODO has bug
131 | def get_nowait(self):
132 | """Equivalent to get(False)."""
133 | return self.get(False)
134 |
135 | def flush(self):
136 | self.__db.delete(self.key)
137 |
138 | if __name__ == '__main__':
139 | q = RedisQueue('test')
140 | # q.flush()
141 | start = time.time()
142 | key = ''
143 | #仅仅set,22972.6616659 条每秒
144 | #set、get、del,大概为8000条每秒
145 | #几乎不随数据量大小而变慢
146 | for i in xrange(100):
147 | key = 'key' + str(i)
148 | q.put({'key': key})
149 | print q.get(block = False, timeout = 1)
150 | end = time.time()
151 | t = end - start
152 | print t
153 | print 100000 / t
154 | print q.qsize()
155 | # q.flush()
156 | # print q.keys()
157 | # r.flushdb()
158 | # print r.keys()
--------------------------------------------------------------------------------
/utils/lib/xlsxwriter/chart_bar.py:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | #
3 | # ChartBar - A class for writing the Excel XLSX Bar charts.
4 | #
5 | # Copyright 2013-2015, John McNamara, jmcnamara@cpan.org
6 | #
7 |
8 | from . import chart
9 | from warnings import warn
10 |
11 |
12 | class ChartBar(chart.Chart):
13 | """
14 | A class for writing the Excel XLSX Bar charts.
15 |
16 |
17 | """
18 |
19 | ###########################################################################
20 | #
21 | # Public API.
22 | #
23 | ###########################################################################
24 |
25 | def __init__(self, options=None):
26 | """
27 | Constructor.
28 |
29 | """
30 | super(ChartBar, self).__init__()
31 |
32 | if options is None:
33 | options = {}
34 |
35 | self.subtype = options.get('subtype')
36 |
37 | if not self.subtype:
38 | self.subtype = 'clustered'
39 |
40 | self.cat_axis_position = 'l'
41 | self.val_axis_position = 'b'
42 | self.horiz_val_axis = 0
43 | self.horiz_cat_axis = 1
44 | self.show_crosses = 0
45 |
46 | # Override and reset the default axis values.
47 | self.x_axis['defaults']['major_gridlines'] = {'visible': 1}
48 | self.y_axis['defaults']['major_gridlines'] = {'visible': 0}
49 |
50 | if self.subtype == 'percent_stacked':
51 | self.x_axis['defaults']['num_format'] = '0%'
52 |
53 | # Set the available data label positions for this chart type.
54 | self.label_position_default = 'outside_end'
55 | self.label_positions = {
56 | 'center': 'ctr',
57 | 'inside_base': 'inBase',
58 | 'inside_end': 'inEnd',
59 | 'outside_end': 'outEnd'}
60 |
61 | self.set_x_axis({})
62 | self.set_y_axis({})
63 |
64 | def combine(self, chart=None):
65 | """
66 | Create a combination chart with a secondary chart.
67 |
68 | Note: Override parent method to add an extra check that is required
69 | for Bar charts to ensure that their combined chart is on a secondary
70 | axis.
71 |
72 | Args:
73 | chart: The secondary chart to combine with the primary chart.
74 |
75 | Returns:
76 | Nothing.
77 |
78 | """
79 | if chart is None:
80 | return
81 |
82 | if not chart.is_secondary:
83 | warn('Charts combined with Bar charts must be on a secondary axis')
84 |
85 | self.combined = chart
86 |
87 | ###########################################################################
88 | #
89 | # Private API.
90 | #
91 | ###########################################################################
92 |
93 | def _write_chart_type(self, args):
94 | # Override the virtual superclass method with a chart specific method.
95 | if args['primary_axes']:
96 | # Reverse X and Y axes for Bar charts.
97 | tmp = self.y_axis
98 | self.y_axis = self.x_axis
99 | self.x_axis = tmp
100 |
101 | if self.y2_axis['position'] == 'r':
102 | self.y2_axis['position'] = 't'
103 |
104 | # Write the c:barChart element.
105 | self._write_bar_chart(args)
106 |
107 | def _write_bar_chart(self, args):
108 | # Write the element.
109 |
110 | if args['primary_axes']:
111 | series = self._get_primary_axes_series()
112 | else:
113 | series = self._get_secondary_axes_series()
114 |
115 | if not len(series):
116 | return
117 |
118 | subtype = self.subtype
119 | if subtype == 'percent_stacked':
120 | subtype = 'percentStacked'
121 |
122 | # Set a default overlap for stacked charts.
123 | if 'stacked' in self.subtype:
124 | if self.series_overlap_1 is None:
125 | self.series_overlap_1 = 100
126 |
127 | self._xml_start_tag('c:barChart')
128 |
129 | # Write the c:barDir element.
130 | self._write_bar_dir()
131 |
132 | # Write the c:grouping element.
133 | self._write_grouping(subtype)
134 |
135 | # Write the c:ser elements.
136 | for data in series:
137 | self._write_ser(data)
138 |
139 | # Write the c:marker element.
140 | self._write_marker_value()
141 |
142 | # Write the c:gapWidth element.
143 | if args['primary_axes']:
144 | self._write_gap_width(self.series_gap_1)
145 | else:
146 | self._write_gap_width(self.series_gap_2)
147 |
148 | # Write the c:overlap element.
149 | if args['primary_axes']:
150 | self._write_overlap(self.series_overlap_1)
151 | else:
152 | self._write_overlap(self.series_overlap_2)
153 |
154 | # Write the c:axId elements
155 | self._write_axis_ids(args)
156 |
157 | self._xml_end_tag('c:barChart')
158 |
159 | ###########################################################################
160 | #
161 | # XML methods.
162 | #
163 | ###########################################################################
164 |
165 | def _write_bar_dir(self):
166 | # Write the element.
167 | val = 'bar'
168 |
169 | attributes = [('val', val)]
170 |
171 | self._xml_empty_tag('c:barDir', attributes)
172 |
173 | def _write_err_dir(self, val):
174 | # Overridden from Chart class since it is not used in Bar charts.
175 | pass
176 |
--------------------------------------------------------------------------------
/app/static/res/css/styles.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | html, body {
18 | font-family: 'Roboto', 'Helvetica', sans-serif;
19 | margin: 0;
20 | padding: 0;
21 | }
22 | .mdl-demo .mdl-layout__header-row {
23 | padding-left: 40px;
24 | }
25 | .mdl-demo .mdl-layout.is-small-screen .mdl-layout__header-row h3 {
26 | font-size: inherit;
27 | }
28 | .mdl-demo .mdl-layout__tab-bar-button {
29 | display: none;
30 | }
31 | .mdl-demo .mdl-layout.is-small-screen .mdl-layout__tab-bar .mdl-button {
32 | display: none;
33 | }
34 | .mdl-demo .mdl-layout:not(.is-small-screen) .mdl-layout__tab-bar,
35 | .mdl-demo .mdl-layout:not(.is-small-screen) .mdl-layout__tab-bar-container {
36 | overflow: visible;
37 | }
38 | .mdl-demo .mdl-layout__tab-bar-container {
39 | height: 64px;
40 | }
41 | .mdl-demo .mdl-layout__tab-bar {
42 | padding: 0;
43 | padding-left: 16px;
44 | box-sizing: border-box;
45 | height: 100%;
46 | width: 100%;
47 | }
48 | .mdl-demo .mdl-layout__tab-bar .mdl-layout__tab {
49 | height: 64px;
50 | line-height: 64px;
51 | }
52 | .mdl-demo .mdl-layout__tab-bar .mdl-layout__tab.is-active::after {
53 | background-color: white;
54 | height: 4px;
55 | }
56 | .mdl-demo main > .mdl-layout__tab-panel {
57 | padding: 8px;
58 | padding-top: 48px;
59 | }
60 | .mdl-demo .mdl-card {
61 | height: auto;
62 | display: flex;
63 | flex-direction: column;
64 | }
65 | .mdl-demo .mdl-card > * {
66 | height: auto;
67 | }
68 | .mdl-demo .mdl-card .mdl-card__supporting-text {
69 | margin: 40px;
70 | flex-grow: 1;
71 | padding: 0;
72 | color: inherit;
73 | width: calc(100% - 80px);
74 | }
75 | .mdl-demo.mdl-demo .mdl-card__supporting-text h4 {
76 | margin-top: 0;
77 | margin-bottom: 20px;
78 | }
79 | .mdl-demo .mdl-card__actions {
80 | margin: 0;
81 | padding: 4px 40px;
82 | color: inherit;
83 | }
84 | .mdl-demo .mdl-card__actions a {
85 | color: #00BCD4;
86 | margin: 0;
87 | }
88 | .mdl-demo .mdl-card__actions a:hover,
89 | .mdl-demo .mdl-card__actions a:active {
90 | color: inherit;
91 | background-color: transparent;
92 | }
93 | .mdl-demo .mdl-card__supporting-text + .mdl-card__actions {
94 | border-top: 1px solid rgba(0, 0, 0, 0.12);
95 | }
96 | .mdl-demo #add {
97 | position: absolute;
98 | right: 40px;
99 | top: 36px;
100 | z-index: 999;
101 | }
102 |
103 | .mdl-demo .mdl-layout__content section:not(:last-of-type) {
104 | position: relative;
105 | margin-bottom: 48px;
106 | }
107 | .mdl-demo section.section--center {
108 | max-width: 860px;
109 | }
110 | .mdl-demo #features section.section--center {
111 | max-width: 620px;
112 | }
113 | .mdl-demo section > header{
114 | display: flex;
115 | align-items: center;
116 | justify-content: center;
117 | }
118 | .mdl-demo section > .section__play-btn {
119 | min-height: 200px;
120 | }
121 | .mdl-demo section > header > .material-icons {
122 | font-size: 3rem;
123 | }
124 | .mdl-demo section > button {
125 | position: absolute;
126 | z-index: 99;
127 | top: 8px;
128 | right: 8px;
129 | }
130 | .mdl-demo section .section__circle {
131 | display: flex;
132 | align-items: center;
133 | justify-content: flex-start;
134 | flex-grow: 0;
135 | flex-shrink: 1;
136 | }
137 | .mdl-demo section .section__text {
138 | flex-grow: 1;
139 | flex-shrink: 0;
140 | padding-top: 8px;
141 | }
142 | .mdl-demo section .section__text h5 {
143 | font-size: inherit;
144 | margin: 0;
145 | margin-bottom: 0.5em;
146 | }
147 | .mdl-demo section .section__text a {
148 | text-decoration: none;
149 | }
150 | .mdl-demo section .section__circle-container > .section__circle-container__circle {
151 | width: 64px;
152 | height: 64px;
153 | border-radius: 32px;
154 | margin: 8px 0;
155 | }
156 | .mdl-demo section.section--footer .section__circle--big {
157 | width: 100px;
158 | height: 100px;
159 | border-radius: 50px;
160 | margin: 8px 32px;
161 | }
162 | .mdl-demo .is-small-screen section.section--footer .section__circle--big {
163 | width: 50px;
164 | height: 50px;
165 | border-radius: 25px;
166 | margin: 8px 16px;
167 | }
168 | .mdl-demo section.section--footer {
169 | padding: 64px 0;
170 | margin: 0 -8px -8px -8px;
171 | }
172 | .mdl-demo section.section--center .section__text:not(:last-child) {
173 | border-bottom: 1px solid rgba(0,0,0,.13);
174 | }
175 | .mdl-demo .mdl-card .mdl-card__supporting-text > h3:first-child {
176 | margin-bottom: 24px;
177 | }
178 | .mdl-demo .mdl-layout__tab-panel:not(#overview) {
179 | background-color: white;
180 | }
181 | .mdl-demo #features section {
182 | margin-bottom: 72px;
183 | }
184 | .mdl-demo #features h4, #features h5 {
185 | margin-bottom: 16px;
186 | }
187 | .mdl-demo .toc {
188 | border-left: 4px solid #C1EEF4;
189 | margin: 24px;
190 | padding: 0;
191 | padding-left: 8px;
192 | display: flex;
193 | flex-direction: column;
194 | }
195 | .mdl-demo .toc h4 {
196 | font-size: 0.9rem;
197 | margin-top: 0;
198 | }
199 | .mdl-demo .toc a {
200 | color: #4DD0E1;
201 | text-decoration: none;
202 | font-size: 16px;
203 | line-height: 28px;
204 | display: block;
205 | }
206 | .mdl-demo .mdl-menu__container {
207 | z-index: 99;
208 | }
209 |
--------------------------------------------------------------------------------
/utils/lib/xlsxwriter/chartsheet.py:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | #
3 | # Chartsheet - A class for writing the Excel XLSX Worksheet file.
4 | #
5 | # Copyright 2013-2015, John McNamara, jmcnamara@cpan.org
6 | #
7 |
8 | from . import worksheet
9 | from .drawing import Drawing
10 |
11 |
12 | class Chartsheet(worksheet.Worksheet):
13 | """
14 | A class for writing the Excel XLSX Chartsheet file.
15 |
16 |
17 | """
18 |
19 | ###########################################################################
20 | #
21 | # Public API.
22 | #
23 | ###########################################################################
24 |
25 | def __init__(self):
26 | """
27 | Constructor.
28 |
29 | """
30 |
31 | super(Chartsheet, self).__init__()
32 |
33 | self.is_chartsheet = True
34 | self.drawing = None
35 | self.chart = None
36 | self.charts = []
37 | self.zoom_scale_normal = 0
38 | self.orientation = 0
39 | self.protection = False
40 |
41 | def set_chart(self, chart):
42 | """
43 | Set the chart object for the chartsheet.
44 | Args:
45 | chart: Chart object.
46 | Returns:
47 | chart: A reference to the chart object.
48 | """
49 | chart.embedded = False
50 | chart.protection = self.protection
51 | self.chart = chart
52 | self.charts.append([0, 0, chart, 0, 0, 1, 1])
53 | return chart
54 |
55 | def protect(self, password='', options=None):
56 | """
57 | Set the password and protection options of the worksheet.
58 |
59 | Args:
60 | password: An optional password string.
61 | options: A dictionary of worksheet objects to protect.
62 |
63 | Returns:
64 | Nothing.
65 |
66 | """
67 | # Overridden from parent worksheet class.
68 | if self.chart:
69 | self.chart.protection = True
70 | else:
71 | self.protection = True
72 |
73 | if not options:
74 | options = {}
75 |
76 | options = options.copy()
77 |
78 | options['sheet'] = False
79 | options['content'] = True
80 | options['scenarios'] = True
81 |
82 | # Call the parent method.
83 | super(Chartsheet, self).protect(password, options)
84 |
85 | ###########################################################################
86 | #
87 | # Private API.
88 | #
89 | ###########################################################################
90 | def _assemble_xml_file(self):
91 | # Assemble and write the XML file.
92 |
93 | # Write the XML declaration.
94 | self._xml_declaration()
95 |
96 | # Write the root worksheet element.
97 | self._write_chartsheet()
98 |
99 | # Write the worksheet properties.
100 | self._write_sheet_pr()
101 |
102 | # Write the sheet view properties.
103 | self._write_sheet_views()
104 |
105 | # Write the sheetProtection element.
106 | self._write_sheet_protection()
107 |
108 | # Write the printOptions element.
109 | self._write_print_options()
110 |
111 | # Write the worksheet page_margins.
112 | self._write_page_margins()
113 |
114 | # Write the worksheet page setup.
115 | self._write_page_setup()
116 |
117 | # Write the headerFooter element.
118 | self._write_header_footer()
119 |
120 | # Write the drawing element.
121 | self._write_drawings()
122 |
123 | # Close the worksheet tag.
124 | self._xml_end_tag('chartsheet')
125 |
126 | # Close the file.
127 | self._xml_close()
128 |
129 | def _prepare_chart(self, index, chart_id, drawing_id):
130 | # Set up chart/drawings.
131 |
132 | self.chart.id = chart_id - 1
133 |
134 | self.drawing = Drawing()
135 | self.drawing.orientation = self.orientation
136 |
137 | self.external_drawing_links.append(['/drawing',
138 | '../drawings/drawing'
139 | + str(drawing_id)
140 | + '.xml'])
141 |
142 | self.drawing_links.append(['/chart',
143 | '../charts/chart'
144 | + str(chart_id)
145 | + '.xml'])
146 |
147 | ###########################################################################
148 | #
149 | # XML methods.
150 | #
151 | ###########################################################################
152 |
153 | def _write_chartsheet(self):
154 | # Write the element. This is the root element.
155 |
156 | schema = 'http://schemas.openxmlformats.org/'
157 | xmlns = schema + 'spreadsheetml/2006/main'
158 | xmlns_r = schema + 'officeDocument/2006/relationships'
159 |
160 | attributes = [
161 | ('xmlns', xmlns),
162 | ('xmlns:r', xmlns_r)]
163 |
164 | self._xml_start_tag('chartsheet', attributes)
165 |
166 | def _write_sheet_pr(self):
167 | # Write the element for Sheet level properties.
168 | attributes = []
169 |
170 | if self.filter_on:
171 | attributes.append(('filterMode', 1))
172 |
173 | if (self.fit_page or self.tab_color):
174 | self._xml_start_tag('sheetPr', attributes)
175 | self._write_tab_color()
176 | self._write_page_set_up_pr()
177 | self._xml_end_tag('sheetPr')
178 | else:
179 | self._xml_empty_tag('sheetPr', attributes)
180 |
--------------------------------------------------------------------------------
/api/app_utils.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年10月29日
4 |
5 | @author: atool
6 | '''
7 | import re
8 | import os, time, datetime, random
9 | import subprocess
10 | from api import api_helpers
11 | from dump import class_dump_utils
12 | import zipfile
13 | from hashlib import md5
14 |
15 | def unzip_ipa(ipa_path, dest_path):
16 | '''
17 | unzip a ipa, and return the zip folder
18 | '''
19 | file_zip = zipfile.ZipFile(ipa_path, 'r')
20 | for f in file_zip.namelist():
21 | file_zip.extract(f, dest_path)
22 | file_zip.close()
23 | return os.path.join(dest_path, 'Payload')
24 |
25 | def get_executable_file(path):
26 | '''
27 | info:从ipa中解压出Payload目录中的xxx.app,扫描其中的文件,寻找 Mach-O 文件的路径
28 | '''
29 |
30 | cmd = u"python -mmacholib find %s" % (path)
31 | out = subprocess.check_output(cmd.split())
32 | if out:
33 | out = out.split()
34 | if out and len(out) > 0:
35 | return os.path.join(path, out[0])
36 | return False
37 |
38 |
39 |
40 | def get_app_strings(app_path, pid):
41 | """
42 | Args:
43 | app : the full path of the Mach-O file in app
44 | Returns:
45 | output : the result of the strings app
46 |
47 | info:strings - 显示文件中的可打印字符
48 | strings 的主要用途是确定非文本文件的包含的文本内容。
49 | """
50 | cmd = "/usr/bin/strings %s" % app_path
51 | output = subprocess.check_output(cmd.split())
52 |
53 | return set(output.split())
54 |
55 |
56 | def get_dump_result(app):
57 | """
58 | get app class-dump result, and cache it
59 | """
60 | dump_result = class_dump_utils.dump_app(app)
61 | return dump_result
62 |
63 | def get_app_variables(dump_result, pid):
64 | "get all variables, properties, and interface name"
65 | interface = re.compile("^@interface (\w*).*")
66 | protocol = re.compile("@protocoli (\w*)")
67 | private = re.compile("^\s*[\w <>]* [*]?(\w*)[\[\]\d]*;")
68 | prop = re.compile("@property\([\w, ]*\) (?:\w+ )*[*]?(\w+); // @synthesize \w*(?:=([\w]*))?;")
69 | res = set()
70 | lines = dump_result.split("\n")
71 | wait_end = False
72 | for line in lines:
73 | l = line.strip()
74 | if l.startswith("}"):
75 | wait_end = False
76 | continue
77 | if wait_end:
78 | r = private.search(l)
79 | if r:
80 | res.add(r.groups()[0])
81 | continue
82 | r = interface.search(l)
83 | if r:
84 | res.add(r.groups()[0])
85 | wait_end = True
86 | continue
87 | r = protocol.search(l)
88 | if r:
89 | res.add(r.groups()[0])
90 | wait_end = True
91 | continue
92 | r = prop.search(l)
93 | if r:
94 | m = r.groups()
95 | res.add(m[0])
96 | res.add("set" + m[0].title() + ":")
97 | #print "set" + m[0].title() + ":"
98 | if m[1] != None:
99 | # res.add("V"+m[1])
100 | res.add(m[1])
101 | return res
102 |
103 |
104 | def get_app_methods(dump_result, pid):
105 | '''
106 | info:获得app中的方法
107 | '''
108 | # dump_result = class_dump_utils.dump_app(app)
109 | methods = api_helpers.extract(dump_result)
110 | #for m in methods:
111 | # ret_methods = ret_methods.union(set(m["methods"]))
112 | #保留class_name信息
113 | return methods
114 |
115 |
116 | def check_architectures(app):
117 | '''
118 | info检查是否支持64位
119 | demo:armv7, arm64, armv7s
120 | '''
121 | from macholib import MachO, mach_o
122 |
123 | m = MachO.MachO(app)
124 | arcs = []
125 | for header in m.headers:
126 | cpu_type = header.header.cputype
127 | cpu_subtype = header.header.cpusubtype
128 | arch = str(mach_o.CPU_TYPE_NAMES.get(cpu_type, cpu_type)).lower()
129 | if cpu_type == 12:
130 | if cpu_subtype == 0:
131 | arch = 'armall'
132 | elif cpu_subtype == 5:
133 | arch = 'armv4t'
134 | elif cpu_subtype == 6:
135 | arch = 'armv6'
136 | elif cpu_subtype == 7:
137 | arch = 'armv5tej'
138 | elif cpu_subtype == 8:
139 | arch = 'arm_xscale'
140 | elif cpu_subtype == 9:
141 | arch = 'armv7'
142 | elif cpu_subtype == 10:
143 | arch = 'armv7f'
144 | elif cpu_subtype == 11:
145 | arch = 'armv7s'
146 | elif cpu_subtype == 12:
147 | arch = 'armv7k'
148 | elif cpu_subtype == 13:
149 | arch = 'armv8'
150 | elif cpu_subtype == 14:
151 | arch = 'armv6m'
152 | elif cpu_subtype == 15:
153 | arch = 'armv7m'
154 | elif cpu_subtype == 16:
155 | arch = 'armv7em'
156 |
157 | elif cpu_type == 16777228:
158 | arch = 'arm64'
159 |
160 | arcs.append(arch)
161 | return arcs
162 |
163 |
164 | #检查app是否被xcode ghost感染
165 | xcode_ghost_keyword = "icloud-analysis.com"
166 | def check_xcode_ghost(app):
167 | cmd = "/usr/bin/strings %s" % app
168 | output = subprocess.check_output(cmd.split())
169 | output = output.replace("\n", "")
170 | output = output.replace(" ", "")
171 | output = output.replace("\t", "")
172 |
173 | return xcode_ghost_keyword in output
174 |
175 | #获得文件的md5值,便于在检测之后,判断检测文件和上传文件是否对应
176 | def file_md5(file_path):
177 | m = md5()
178 | f = open(file_path, 'rb')
179 | m.update(f.read())
180 | return m.hexdigest()
181 |
182 | def get_unique_str():
183 | #随机的名字,可以用于上传文件等等不重复,但有一定时间意义的名字
184 | datetime_str = time.strftime('%Y%m%d%H%M%S',time.localtime())
185 | return datetime_str + str(datetime.datetime.now().microsecond / 1000) + str(random.randint(0, 1000))
186 |
--------------------------------------------------------------------------------
/utils/lib/xlsxwriter/table.py:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | #
3 | # Table - A class for writing the Excel XLSX Worksheet file.
4 | #
5 | # Copyright 2013-2015, John McNamara, jmcnamara@cpan.org
6 | #
7 |
8 | from . import xmlwriter
9 |
10 |
11 | class Table(xmlwriter.XMLwriter):
12 | """
13 | A class for writing the Excel XLSX Table file.
14 |
15 |
16 | """
17 |
18 | ###########################################################################
19 | #
20 | # Public API.
21 | #
22 | ###########################################################################
23 |
24 | def __init__(self):
25 | """
26 | Constructor.
27 |
28 | """
29 |
30 | super(Table, self).__init__()
31 |
32 | self.properties = {}
33 |
34 | ###########################################################################
35 | #
36 | # Private API.
37 | #
38 | ###########################################################################
39 |
40 | def _assemble_xml_file(self):
41 | # Assemble and write the XML file.
42 |
43 | # Write the XML declaration.
44 | self._xml_declaration()
45 |
46 | # Write the table element.
47 | self._write_table()
48 |
49 | # Write the autoFilter element.
50 | self._write_auto_filter()
51 |
52 | # Write the tableColumns element.
53 | self._write_table_columns()
54 |
55 | # Write the tableStyleInfo element.
56 | self._write_table_style_info()
57 |
58 | # Close the table tag.
59 | self._xml_end_tag('table')
60 |
61 | # Close the file.
62 | self._xml_close()
63 |
64 | def _set_properties(self, properties):
65 | # Set the document properties.
66 | self.properties = properties
67 |
68 | ###########################################################################
69 | #
70 | # XML methods.
71 | #
72 | ###########################################################################
73 |
74 | def _write_table(self):
75 | # Write the element.
76 | schema = 'http://schemas.openxmlformats.org/'
77 | xmlns = schema + 'spreadsheetml/2006/main'
78 | table_id = self.properties['id']
79 | name = self.properties['name']
80 | display_name = self.properties['name']
81 | ref = self.properties['range']
82 | totals_row_shown = self.properties['totals_row_shown']
83 | header_row_count = self.properties['header_row_count']
84 |
85 | attributes = [
86 | ('xmlns', xmlns),
87 | ('id', table_id),
88 | ('name', name),
89 | ('displayName', display_name),
90 | ('ref', ref),
91 | ]
92 |
93 | if not header_row_count:
94 | attributes.append(('headerRowCount', 0))
95 |
96 | if totals_row_shown:
97 | attributes.append(('totalsRowCount', 1))
98 | else:
99 | attributes.append(('totalsRowShown', 0))
100 |
101 | self._xml_start_tag('table', attributes)
102 |
103 | def _write_auto_filter(self):
104 | # Write the element.
105 | autofilter = self.properties.get('autofilter', 0)
106 |
107 | if not autofilter:
108 | return
109 |
110 | attributes = [('ref', autofilter,)]
111 |
112 | self._xml_empty_tag('autoFilter', attributes)
113 |
114 | def _write_table_columns(self):
115 | # Write the element.
116 | columns = self.properties['columns']
117 |
118 | count = len(columns)
119 |
120 | attributes = [('count', count)]
121 |
122 | self._xml_start_tag('tableColumns', attributes)
123 |
124 | for col_data in columns:
125 | # Write the tableColumn element.
126 | self._write_table_column(col_data)
127 |
128 | self._xml_end_tag('tableColumns')
129 |
130 | def _write_table_column(self, col_data):
131 | # Write the element.
132 | attributes = [
133 | ('id', col_data['id']),
134 | ('name', col_data['name']),
135 | ]
136 |
137 | if col_data.get('total_string'):
138 | attributes.append(('totalsRowLabel', col_data['total_string']))
139 | elif col_data.get('total_function'):
140 | attributes.append(('totalsRowFunction',
141 | col_data['total_function']))
142 |
143 | if 'format' in col_data and col_data['format'] is not None:
144 | attributes.append(('dataDxfId', col_data['format']))
145 |
146 | if col_data.get('formula'):
147 | self._xml_start_tag('tableColumn', attributes)
148 |
149 | # Write the calculatedColumnFormula element.
150 | self._write_calculated_column_formula(col_data['formula'])
151 |
152 | self._xml_end_tag('tableColumn')
153 | else:
154 | self._xml_empty_tag('tableColumn', attributes)
155 |
156 | def _write_table_style_info(self):
157 | # Write the element.
158 | props = self.properties
159 |
160 | name = props['style']
161 | show_first_column = 0 + props['show_first_col']
162 | show_last_column = 0 + props['show_last_col']
163 | show_row_stripes = 0 + props['show_row_stripes']
164 | show_column_stripes = 0 + props['show_col_stripes']
165 |
166 | attributes = [
167 | ('name', name),
168 | ('showFirstColumn', show_first_column),
169 | ('showLastColumn', show_last_column),
170 | ('showRowStripes', show_row_stripes),
171 | ('showColumnStripes', show_column_stripes),
172 | ]
173 |
174 | self._xml_empty_tag('tableStyleInfo', attributes)
175 |
176 | def _write_calculated_column_formula(self, formula):
177 | # Write the element.
178 | self._xml_data_element('calculatedColumnFormula', formula)
179 |
--------------------------------------------------------------------------------
/utils/lib/xlsxwriter/comments.py:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | #
3 | # Comments - A class for writing the Excel XLSX Worksheet file.
4 | #
5 | # Copyright 2013-2015, John McNamara, jmcnamara@cpan.org
6 | #
7 |
8 | import re
9 |
10 | from . import xmlwriter
11 | from .utility import xl_rowcol_to_cell
12 |
13 |
14 | class Comments(xmlwriter.XMLwriter):
15 | """
16 | A class for writing the Excel XLSX Comments file.
17 |
18 |
19 | """
20 |
21 | ###########################################################################
22 | #
23 | # Public API.
24 | #
25 | ###########################################################################
26 |
27 | def __init__(self):
28 | """
29 | Constructor.
30 |
31 | """
32 |
33 | super(Comments, self).__init__()
34 | self.author_ids = {}
35 |
36 | ###########################################################################
37 | #
38 | # Private API.
39 | #
40 | ###########################################################################
41 |
42 | def _assemble_xml_file(self, comments_data=[]):
43 | # Assemble and write the XML file.
44 |
45 | # Write the XML declaration.
46 | self._xml_declaration()
47 |
48 | # Write the comments element.
49 | self._write_comments()
50 |
51 | # Write the authors element.
52 | self._write_authors(comments_data)
53 |
54 | # Write the commentList element.
55 | self._write_comment_list(comments_data)
56 |
57 | self._xml_end_tag('comments')
58 |
59 | # Close the file.
60 | self._xml_close()
61 |
62 | ###########################################################################
63 | #
64 | # XML methods.
65 | #
66 | ###########################################################################
67 |
68 | def _write_comments(self):
69 | # Write the element.
70 | xmlns = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'
71 |
72 | attributes = [('xmlns', xmlns)]
73 |
74 | self._xml_start_tag('comments', attributes)
75 |
76 | def _write_authors(self, comment_data):
77 | # Write the element.
78 | author_count = 0
79 |
80 | self._xml_start_tag('authors')
81 |
82 | for comment in comment_data:
83 | author = comment[3]
84 |
85 | if author is not None and author not in self.author_ids:
86 | # Store the author id.
87 | self.author_ids[author] = author_count
88 | author_count += 1
89 |
90 | # Write the author element.
91 | self._write_author(author)
92 |
93 | self._xml_end_tag('authors')
94 |
95 | def _write_author(self, data):
96 | # Write the element.
97 | self._xml_data_element('author', data)
98 |
99 | def _write_comment_list(self, comment_data):
100 | # Write the element.
101 | self._xml_start_tag('commentList')
102 |
103 | for comment in comment_data:
104 | row = comment[0]
105 | col = comment[1]
106 | text = comment[2]
107 | author = comment[3]
108 |
109 | # Look up the author id.
110 | author_id = None
111 | if author is not None:
112 | author_id = self.author_ids[author]
113 |
114 | # Write the comment element.
115 | self._write_comment(row, col, text, author_id)
116 |
117 | self._xml_end_tag('commentList')
118 |
119 | def _write_comment(self, row, col, text, author_id):
120 | # Write the element.
121 | ref = xl_rowcol_to_cell(row, col)
122 |
123 | attributes = [('ref', ref)]
124 |
125 | if author_id is not None:
126 | attributes.append(('authorId', author_id))
127 |
128 | self._xml_start_tag('comment', attributes)
129 |
130 | # Write the text element.
131 | self._write_text(text)
132 |
133 | self._xml_end_tag('comment')
134 |
135 | def _write_text(self, text):
136 | # Write the element.
137 | self._xml_start_tag('text')
138 |
139 | # Write the text r element.
140 | self._write_text_r(text)
141 |
142 | self._xml_end_tag('text')
143 |
144 | def _write_text_r(self, text):
145 | # Write the element.
146 | self._xml_start_tag('r')
147 |
148 | # Write the rPr element.
149 | self._write_r_pr()
150 |
151 | # Write the text r element.
152 | self._write_text_t(text)
153 |
154 | self._xml_end_tag('r')
155 |
156 | def _write_text_t(self, text):
157 | # Write the text element.
158 | attributes = []
159 |
160 | if re.search('^\s', text) or re.search('\s$', text):
161 | attributes.append(('xml:space', 'preserve'))
162 |
163 | self._xml_data_element('t', text, attributes)
164 |
165 | def _write_r_pr(self):
166 | # Write the element.
167 | self._xml_start_tag('rPr')
168 |
169 | # Write the sz element.
170 | self._write_sz()
171 |
172 | # Write the color element.
173 | self._write_color()
174 |
175 | # Write the rFont element.
176 | self._write_r_font()
177 |
178 | # Write the family element.
179 | self._write_family()
180 |
181 | self._xml_end_tag('rPr')
182 |
183 | def _write_sz(self):
184 | # Write the element.
185 | attributes = [('val', 8)]
186 |
187 | self._xml_empty_tag('sz', attributes)
188 |
189 | def _write_color(self):
190 | # Write the element.
191 | attributes = [('indexed', 81)]
192 |
193 | self._xml_empty_tag('color', attributes)
194 |
195 | def _write_r_font(self):
196 | # Write the element.
197 | attributes = [('val', 'Tahoma')]
198 |
199 | self._xml_empty_tag('rFont', attributes)
200 |
201 | def _write_family(self):
202 | # Write the element.
203 | attributes = [('val', 2)]
204 |
205 | self._xml_empty_tag('family', attributes)
206 |
--------------------------------------------------------------------------------
/app/utils/IpaParse.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年5月18日
4 |
5 | @author: atool
6 | '''
7 | import json
8 | import os
9 | import re
10 | import tempfile
11 | import zipfile
12 |
13 | from biplist import readPlist
14 |
15 | class IpaParse(object):
16 | '''
17 | DEMO
18 | parse = IpaParse(filename)
19 | print parse.app_name() #app 名称
20 | print parse.bundle_identifier() #package
21 | print parse.version()
22 | print parse.target_os_version() #target version
23 | print parse.minimum_os_version() #min version
24 | print parse.icon_file_name() # icon name
25 | print parse.icon_file_path() #path
26 |
27 | print parse.mv_icon_to('test.png') #ico复制到指定位置,图片被加密暂时无法处理
28 | '''
29 | ipa_file_path = None
30 | ipa_base_path = None
31 |
32 | plist_temp_file = None
33 | plist_info_list = None
34 |
35 | def __init__(self, ipa_file_path):
36 | '''
37 | Constructor
38 | '''
39 | self.ipa_file_path = ipa_file_path
40 |
41 |
42 | def _get_plist_temp_file(self):
43 | self.plist_temp_file = ''
44 |
45 | zfile = zipfile.ZipFile(self.ipa_file_path)
46 | zip_name_list = zfile.namelist()
47 | for name in zip_name_list:
48 | if ".app/Info.plist" in name:
49 | print name
50 | tup = tempfile.mkstemp(suffix = '.plist')
51 | fd = os.fdopen(tup[0], "w")
52 | fd.write(zfile.read(name))
53 | fd.close()
54 | self.plist_temp_file = tup[1]
55 | zfile.close()
56 |
57 | return self.plist_temp_file
58 |
59 | def _parse_plist(self):
60 | try:
61 | if self.plist_temp_file == None:
62 | self._get_plist_temp_file()
63 |
64 | if self.plist_temp_file == '':
65 | self.plist_info_list = {}
66 | return False
67 |
68 | self.plist_info_list = readPlist(self.plist_temp_file)
69 | os.remove(self.plist_temp_file)
70 | return self.plist_info_list
71 | except Exception, e:
72 | print "Not a plist:", e
73 | self.plist_info_list = {}
74 | return False
75 |
76 | def _check(self):
77 | if self.plist_info_list == None:
78 | self._parse_plist()
79 |
80 | def all_info(self):
81 | self._check()
82 | return self.plist_info_list
83 |
84 | def app_name(self):
85 | self._check()
86 | if 'CFBundleDisplayName' in self.plist_info_list:
87 | return self.plist_info_list['CFBundleDisplayName']
88 | elif 'CFBundleName' in self.plist_info_list:
89 | return self.plist_info_list['CFBundleName']
90 | return None
91 |
92 | def bundle_identifier(self):
93 | self._check()
94 | if 'CFBundleIdentifier' in self.plist_info_list:
95 | return self.plist_info_list['CFBundleIdentifier']
96 | return ''
97 |
98 | def target_os_version(self):
99 | self._check()
100 | if 'DTPlatformVersion' in self.plist_info_list:
101 | return re.findall('[\d\.]*', self.plist_info_list['DTPlatformVersion'])[0]
102 | return ''
103 |
104 | def minimum_os_version(self):
105 | self._check()
106 | if 'MinimumOSVersion' in self.plist_info_list:
107 | return re.findall('[\d\.]*', self.plist_info_list['MinimumOSVersion'])[0]
108 | return ''
109 |
110 | def version(self):
111 | self._check()
112 | if 'CFBundleVersion' in self.plist_info_list:
113 | return self.plist_info_list['CFBundleVersion']
114 | return ''
115 |
116 | def icon_file_name(self):
117 | if 'CFBundleIcons' in self.plist_info_list and \
118 | 'CFBundlePrimaryIcon' in self.plist_info_list["CFBundleIcons"] and \
119 | 'CFBundleIconFiles' in self.plist_info_list["CFBundleIcons"]["CFBundlePrimaryIcon"]:
120 | icons = self.plist_info_list["CFBundleIcons"]["CFBundlePrimaryIcon"]['CFBundleIconFiles']
121 | if icons != None and len(icons) > 0:
122 | return icons[len(icons) - 1]
123 | elif 'CFBundleIcons~ipad' in self.plist_info_list and \
124 | 'CFBundlePrimaryIcon' in self.plist_info_list["CFBundleIcons~ipad"] and \
125 | 'CFBundleIconFiles' in self.plist_info_list["CFBundleIcons~ipad"]["CFBundlePrimaryIcon"]:
126 | icons = self.plist_info_list["CFBundleIcons~ipad"]["CFBundlePrimaryIcon"]['CFBundleIconFiles']
127 | if icons != None and len(icons) > 0:
128 | return icons[len(icons) - 1]
129 | else:
130 | return False
131 |
132 | def icon_file_path(self):
133 | icon_file_name = self.icon_file_name()
134 | if icon_file_name:
135 | zfile = zipfile.ZipFile(self.ipa_file_path)
136 | zip_name_list = zfile.namelist()
137 | for name in zip_name_list:
138 | tempkey = ".app/" + icon_file_name
139 | if tempkey in name:
140 | zfile.close()
141 | return name
142 | zfile.close()
143 |
144 | return False
145 |
146 | def mv_icon_to(self, file_name):
147 | icon_path = self.icon_file_path()
148 | if icon_path:
149 | zfile = zipfile.ZipFile(self.ipa_file_path)
150 |
151 | icon_file = open(file_name, "wb")
152 | icon_file.write(zfile.read(icon_path))
153 | icon_file.close()
154 | zfile.close()
155 | return True
156 |
157 | return False
158 |
159 | if __name__ == '__main__':
160 | parse = IpaParse(u'C:\\Users\\atool\\Desktop\\H28_150514121833_resign.ipa')
161 |
162 | print json.dumps(parse.all_info(), default = lambda o: o.__dict__)
163 | print parse.app_name()
164 | print parse.bundle_identifier()
165 | print parse.target_os_version()
166 | print parse.minimum_os_version()
167 | print parse.icon_file_name()
168 | print parse.icon_file_path()
169 | print parse.mv_icon_to('test.png')
170 |
--------------------------------------------------------------------------------
/utils/lib/xlsxwriter/app.py:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | #
3 | # App - A class for writing the Excel XLSX App file.
4 | #
5 | # Copyright 2013-2015, John McNamara, jmcnamara@cpan.org
6 | #
7 |
8 | # Package imports.
9 | from . import xmlwriter
10 |
11 |
12 | class App(xmlwriter.XMLwriter):
13 | """
14 | A class for writing the Excel XLSX App file.
15 |
16 |
17 | """
18 |
19 | ###########################################################################
20 | #
21 | # Public API.
22 | #
23 | ###########################################################################
24 |
25 | def __init__(self):
26 | """
27 | Constructor.
28 |
29 | """
30 |
31 | super(App, self).__init__()
32 |
33 | self.part_names = []
34 | self.heading_pairs = []
35 | self.properties = {}
36 |
37 | def _add_part_name(self, part_name):
38 | # Add the name of a workbook Part such as 'Sheet1' or 'Print_Titles'.
39 | self.part_names.append(part_name)
40 |
41 | def _add_heading_pair(self, heading_pair):
42 | # Add the name of a workbook Heading Pair such as 'Worksheets',
43 | # 'Charts' or 'Named Ranges'.
44 |
45 | # Ignore empty pairs such as chartsheets.
46 | if not heading_pair[1]:
47 | return
48 |
49 | self.heading_pairs.append(('lpstr', heading_pair[0]))
50 | self.heading_pairs.append(('i4', heading_pair[1]))
51 |
52 | def _set_properties(self, properties):
53 | # Set the document properties.
54 | self.properties = properties
55 |
56 | ###########################################################################
57 | #
58 | # Private API.
59 | #
60 | ###########################################################################
61 |
62 | def _assemble_xml_file(self):
63 | # Assemble and write the XML file.
64 |
65 | # Write the XML declaration.
66 | self._xml_declaration()
67 |
68 | self._write_properties()
69 | self._write_application()
70 | self._write_doc_security()
71 | self._write_scale_crop()
72 | self._write_heading_pairs()
73 | self._write_titles_of_parts()
74 | self._write_manager()
75 | self._write_company()
76 | self._write_links_up_to_date()
77 | self._write_shared_doc()
78 | self._write_hyperlinks_changed()
79 | self._write_app_version()
80 |
81 | self._xml_end_tag('Properties')
82 |
83 | # Close the file.
84 | self._xml_close()
85 |
86 | ###########################################################################
87 | #
88 | # XML methods.
89 | #
90 | ###########################################################################
91 |
92 | def _write_properties(self):
93 | # Write the element.
94 | schema = 'http://schemas.openxmlformats.org/officeDocument/2006/'
95 | xmlns = schema + 'extended-properties'
96 | xmlns_vt = schema + 'docPropsVTypes'
97 |
98 | attributes = [
99 | ('xmlns', xmlns),
100 | ('xmlns:vt', xmlns_vt),
101 | ]
102 |
103 | self._xml_start_tag('Properties', attributes)
104 |
105 | def _write_application(self):
106 | # Write the element.
107 | self._xml_data_element('Application', 'Microsoft Excel')
108 |
109 | def _write_doc_security(self):
110 | # Write the element.
111 | self._xml_data_element('DocSecurity', '0')
112 |
113 | def _write_scale_crop(self):
114 | # Write the element.
115 | self._xml_data_element('ScaleCrop', 'false')
116 |
117 | def _write_heading_pairs(self):
118 | # Write the element.
119 | self._xml_start_tag('HeadingPairs')
120 | self._write_vt_vector('variant', self.heading_pairs)
121 | self._xml_end_tag('HeadingPairs')
122 |
123 | def _write_titles_of_parts(self):
124 | # Write the element.
125 | parts_data = []
126 |
127 | self._xml_start_tag('TitlesOfParts')
128 |
129 | for part_name in self.part_names:
130 | parts_data.append(('lpstr', part_name))
131 |
132 | self._write_vt_vector('lpstr', parts_data)
133 |
134 | self._xml_end_tag('TitlesOfParts')
135 |
136 | def _write_vt_vector(self, base_type, vector_data):
137 | # Write the element.
138 | attributes = [
139 | ('size', len(vector_data)),
140 | ('baseType', base_type),
141 | ]
142 |
143 | self._xml_start_tag('vt:vector', attributes)
144 |
145 | for vt_data in vector_data:
146 | if base_type == 'variant':
147 | self._xml_start_tag('vt:variant')
148 |
149 | self._write_vt_data(vt_data)
150 |
151 | if base_type == 'variant':
152 | self._xml_end_tag('vt:variant')
153 |
154 | self._xml_end_tag('vt:vector')
155 |
156 | def _write_vt_data(self, vt_data):
157 | # Write the elements such as and .
158 | self._xml_data_element("vt:%s" % vt_data[0], vt_data[1])
159 |
160 | def _write_company(self):
161 | company = self.properties.get('company', '')
162 |
163 | self._xml_data_element('Company', company)
164 |
165 | def _write_manager(self):
166 | # Write the element.
167 | if 'manager' not in self.properties:
168 | return
169 |
170 | self._xml_data_element('Manager', self.properties['manager'])
171 |
172 | def _write_links_up_to_date(self):
173 | # Write the element.
174 | self._xml_data_element('LinksUpToDate', 'false')
175 |
176 | def _write_shared_doc(self):
177 | # Write the element.
178 | self._xml_data_element('SharedDoc', 'false')
179 |
180 | def _write_hyperlinks_changed(self):
181 | # Write the element.
182 | self._xml_data_element('HyperlinksChanged', 'false')
183 |
184 | def _write_app_version(self):
185 | # Write the element.
186 | self._xml_data_element('AppVersion', '12.0000')
187 |
--------------------------------------------------------------------------------
/utils/lib/xlsxwriter/core.py:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | #
3 | # Core - A class for writing the Excel XLSX Worksheet file.
4 | #
5 | # Copyright 2013-2015, John McNamara, jmcnamara@cpan.org
6 | #
7 |
8 | # Standard packages.
9 | from datetime import datetime
10 |
11 | # Package imports.
12 | from . import xmlwriter
13 |
14 |
15 | class Core(xmlwriter.XMLwriter):
16 | """
17 | A class for writing the Excel XLSX Core file.
18 |
19 |
20 | """
21 |
22 | ###########################################################################
23 | #
24 | # Public API.
25 | #
26 | ###########################################################################
27 |
28 | def __init__(self):
29 | """
30 | Constructor.
31 |
32 | """
33 |
34 | super(Core, self).__init__()
35 |
36 | self.properties = {}
37 |
38 | ###########################################################################
39 | #
40 | # Private API.
41 | #
42 | ###########################################################################
43 |
44 | def _assemble_xml_file(self):
45 | # Assemble and write the XML file.
46 |
47 | # Write the XML declaration.
48 | self._xml_declaration()
49 |
50 | self._write_cp_core_properties()
51 | self._write_dc_title()
52 | self._write_dc_subject()
53 | self._write_dc_creator()
54 | self._write_cp_keywords()
55 | self._write_dc_description()
56 | self._write_cp_last_modified_by()
57 | self._write_dcterms_created()
58 | self._write_dcterms_modified()
59 | self._write_cp_category()
60 | self._write_cp_content_status()
61 |
62 | self._xml_end_tag('cp:coreProperties')
63 |
64 | # Close the file.
65 | self._xml_close()
66 |
67 | def _set_properties(self, properties):
68 | # Set the document properties.
69 | self.properties = properties
70 |
71 | def _localtime_to_iso8601_date(self, date):
72 | # Convert to a ISO 8601 style "2010-01-01T00:00:00Z" date.
73 | if not date:
74 | date = datetime.now()
75 |
76 | return date.strftime("%Y-%m-%dT%H:%M:%SZ")
77 |
78 | ###########################################################################
79 | #
80 | # XML methods.
81 | #
82 | ###########################################################################
83 |
84 | def _write_cp_core_properties(self):
85 | # Write the element.
86 |
87 | xmlns_cp = ('http://schemas.openxmlformats.org/package/2006/' +
88 | 'metadata/core-properties')
89 | xmlns_dc = 'http://purl.org/dc/elements/1.1/'
90 | xmlns_dcterms = 'http://purl.org/dc/terms/'
91 | xmlns_dcmitype = 'http://purl.org/dc/dcmitype/'
92 | xmlns_xsi = 'http://www.w3.org/2001/XMLSchema-instance'
93 |
94 | attributes = [
95 | ('xmlns:cp', xmlns_cp),
96 | ('xmlns:dc', xmlns_dc),
97 | ('xmlns:dcterms', xmlns_dcterms),
98 | ('xmlns:dcmitype', xmlns_dcmitype),
99 | ('xmlns:xsi', xmlns_xsi),
100 | ]
101 |
102 | self._xml_start_tag('cp:coreProperties', attributes)
103 |
104 | def _write_dc_creator(self):
105 | # Write the element.
106 | data = self.properties.get('author', '')
107 |
108 | self._xml_data_element('dc:creator', data)
109 |
110 | def _write_cp_last_modified_by(self):
111 | # Write the element.
112 | data = self.properties.get('author', '')
113 |
114 | self._xml_data_element('cp:lastModifiedBy', data)
115 |
116 | def _write_dcterms_created(self):
117 | # Write the element.
118 | date = self.properties.get('created', datetime.now())
119 |
120 | xsi_type = 'dcterms:W3CDTF'
121 |
122 | date = self._localtime_to_iso8601_date(date)
123 |
124 | attributes = [('xsi:type', xsi_type,)]
125 |
126 | self._xml_data_element('dcterms:created', date, attributes)
127 |
128 | def _write_dcterms_modified(self):
129 | # Write the element.
130 | date = self.properties.get('created', datetime.now())
131 |
132 | xsi_type = 'dcterms:W3CDTF'
133 |
134 | date = self._localtime_to_iso8601_date(date)
135 |
136 | attributes = [('xsi:type', xsi_type,)]
137 |
138 | self._xml_data_element('dcterms:modified', date, attributes)
139 |
140 | def _write_dc_title(self):
141 | # Write the element.
142 | if 'title' in self.properties:
143 | data = self.properties['title']
144 | else:
145 | return
146 |
147 | self._xml_data_element('dc:title', data)
148 |
149 | def _write_dc_subject(self):
150 | # Write the element.
151 | if 'subject' in self.properties:
152 | data = self.properties['subject']
153 | else:
154 | return
155 |
156 | self._xml_data_element('dc:subject', data)
157 |
158 | def _write_cp_keywords(self):
159 | # Write the element.
160 | if 'keywords' in self.properties:
161 | data = self.properties['keywords']
162 | else:
163 | return
164 |
165 | self._xml_data_element('cp:keywords', data)
166 |
167 | def _write_dc_description(self):
168 | # Write the element.
169 | if 'comments' in self.properties:
170 | data = self.properties['comments']
171 | else:
172 | return
173 |
174 | self._xml_data_element('dc:description', data)
175 |
176 | def _write_cp_category(self):
177 | # Write the element.
178 | if 'category' in self.properties:
179 | data = self.properties['category']
180 | else:
181 | return
182 |
183 | self._xml_data_element('cp:category', data)
184 |
185 | def _write_cp_content_status(self):
186 | # Write the element.
187 | if 'status' in self.properties:
188 | data = self.properties['status']
189 | else:
190 | return
191 |
192 | self._xml_data_element('cp:contentStatus', data)
193 |
--------------------------------------------------------------------------------
/app/dbs/inc/Mysql.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年8月21日
4 |
5 | @author: atool
6 | '''
7 |
8 | import MySQLdb
9 | from app import db_config
10 | import time
11 |
12 | import sys
13 | from app.wraps.mysql_escape_warp import mysql_escape
14 |
15 | reload(sys)
16 | sys.setdefaultencoding('utf8')
17 |
18 | @mysql_escape
19 | def dict_2_sql_conditions(dict_param):
20 | conditions = []
21 | p_keys = dict_param.keys()
22 | for k in p_keys:
23 | conditions.append(k + " = '" + str(dict_param[k]) + "'")
24 |
25 | return " and ".join(conditions)
26 |
27 | @mysql_escape
28 | def dict_2_insert_sql(table_name, dict_param):
29 | key = []
30 | val = []
31 | p_keys = dict_param.keys()
32 | for k in p_keys:
33 | key.append(k)
34 | val.append("'" + str(dict_param[k]) + "'")
35 |
36 | key = ",".join(key)
37 | val = ",".join(val)
38 | sql = "insert into " + table_name + "("+ key+") values (" + val + ");"
39 | return sql
40 |
41 | class Mysql():
42 | #对象属性
43 | #连接
44 | conn = None
45 | #数据游标
46 | cursor = None
47 |
48 | #构造函数
49 | def __init__(self, host = db_config['DB_HOST'],
50 | port = db_config['DB_PORT'],
51 | user = db_config['DB_USER'],
52 | passwd = db_config['DB_PSW'],
53 | db = db_config['DB_NAME'],
54 | charset = db_config['DB_CHARSET']):
55 | self.host = host
56 | self.port = port
57 | self.user = user
58 | self.passwd = passwd
59 | self.db = db
60 | self.charset = charset
61 |
62 | self.__connect()
63 |
64 |
65 | def __connect(self):
66 | try:
67 | self.conn = MySQLdb.connect(host = self.host, port = self.port, user = self.user, passwd = self.passwd, db = self.db, charset = self.charset)
68 | #字典形式
69 | self.cursor = self.conn.cursor(cursorclass = MySQLdb.cursors.DictCursor)
70 | # print("Mysql Connect to %s: %s" % (self.host, str(self.port)))
71 | except MySQLdb.Error as e:
72 | print("Mysql Error %s: %s" % (self.host, e.args[1]))
73 |
74 | def exec_select(self, sql, params):
75 | '''
76 | ps:执行查询类型的sql语句
77 | '''
78 | try:
79 | self.cursor.execute(sql, params)
80 | result_set = self.cursor.fetchall()
81 | return result_set
82 | except MySQLdb.Error as e:
83 | print("Mysql Error:%s\nSQL:%s" %(e, sql))
84 | return False
85 |
86 | def exec_select_one(self, sql, params):
87 | '''
88 | ps:执行查询类型的sql语句
89 | '''
90 | try:
91 | self.cursor.execute(sql, params)
92 | result_set = self.cursor.fetchone()
93 | return result_set
94 | except MySQLdb.Error as e:
95 | print("Mysql Error:%s\nSQL:%s" %(e, sql))
96 | return False
97 |
98 | def exec_insert(self, sql, params):
99 | '''
100 | ps:执行插入类sql语句
101 | '''
102 | try:
103 | # 执行sql语句
104 | self.cursor.execute(sql, params)
105 | # 提交到数据库执行
106 | insert_id = self.conn.insert_id()
107 | self.conn.commit()
108 | return insert_id
109 | except MySQLdb.Error as e:
110 | print("Mysql Error:%s\nSQL:%s" %(e, sql))
111 | self.conn.rollback()
112 | return False
113 |
114 | def exec_insert_dict(self, table_name, dict_param):
115 | '''
116 | ps:执行插入数据,数据由一个dict给定,key为column名称
117 | '''
118 | try:
119 | sql = dict_2_insert_sql(table_name, dict_param)
120 | return self.exec_insert(sql, ())
121 | except MySQLdb.Error as e:
122 | print("Mysql Error:%s\nSQL:%s" %(e, sql))
123 | self.conn.rollback()
124 | return False
125 |
126 | def exec_update(self, sql, params):
127 | '''
128 | ps:执行更新类sql语句
129 | '''
130 | try:
131 | # 执行sql语句
132 | self.cursor.execute(sql, params)
133 | row_count = self.cursor.rowcount
134 | # 提交到数据库执行
135 | self.conn.commit()
136 | if row_count == False:
137 | row_count = True
138 | return row_count
139 | except MySQLdb.Error as e:
140 | print("Mysql Error:%s\nSQL:%s" %(e, sql))
141 | self.conn.rollback()
142 | return False
143 | ###################
144 | ###################
145 | def exec_sql(self, sql, params):
146 | try:
147 | n = self.cursor.execute(sql, params)
148 | return n
149 | except MySQLdb.Error as e:
150 | print("Mysql Error:%s\nSQL:%s" %(e, sql))
151 |
152 | def get_last_insert_id(self):
153 | '''
154 | ps:最后插入行id
155 | '''
156 | return self.conn.insert_id()
157 |
158 | def get_influence_row_count(self):
159 | '''
160 | ps:影响函数
161 | '''
162 | return self.cursor.rowcount
163 | #for transation
164 |
165 | def commit(self):
166 | '''
167 | PS:事物完成之后,commit
168 | '''
169 | self.conn.commit()
170 |
171 | # @check_connect
172 | def rollback(self):
173 | '''
174 | PS:事物失败之后,回退
175 | '''
176 | self.conn.rollback()
177 | #end for transation
178 | ###################
179 |
180 | def close(self):
181 | if self.cursor:
182 | self.cursor.close()
183 | self.cursor = None
184 | if self.conn:
185 | self.conn.close()
186 | self.conn = None
187 |
188 | #test
189 | if __name__ == '__main__':
190 | start = time.time()
191 | for i in xrange(100):
192 | sql = "insert into abtest_users(user_type) values(%s)"
193 | params = (str(i), )
194 | Mysql().exec_insert(sql, params)
195 |
196 | end = time.time()
197 | print '多连接:', 100 / (end - start)
198 |
199 | start = time.time()
200 | my = Mysql()
201 | for i in xrange(100):
202 | sql = "insert into abtest_users(user_type) values(%s)"
203 | params = (str(i), )
204 | my.exec_insert(sql, params)
205 |
206 | end = time.time()
207 | print '单连接:', 100 / (end - start)
208 |
--------------------------------------------------------------------------------
/utils/lib/xlsxwriter/chart_pie.py:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | #
3 | # ChartPie - A class for writing the Excel XLSX Pie charts.
4 | #
5 | # Copyright 2013-2015, John McNamara, jmcnamara@cpan.org
6 | #
7 |
8 | from warnings import warn
9 | from . import chart
10 |
11 |
12 | class ChartPie(chart.Chart):
13 | """
14 | A class for writing the Excel XLSX Pie charts.
15 |
16 |
17 | """
18 |
19 | ###########################################################################
20 | #
21 | # Public API.
22 | #
23 | ###########################################################################
24 |
25 | def __init__(self, options=None):
26 | """
27 | Constructor.
28 |
29 | """
30 | super(ChartPie, self).__init__()
31 |
32 | if options is None:
33 | options = {}
34 |
35 | self.vary_data_color = 1
36 | self.rotation = 0
37 |
38 | # Set the available data label positions for this chart type.
39 | self.label_position_default = 'best_fit'
40 | self.label_positions = {
41 | 'center': 'ctr',
42 | 'inside_end': 'inEnd',
43 | 'outside_end': 'outEnd',
44 | 'best_fit': 'bestFit'}
45 |
46 | def set_rotation(self, rotation):
47 | """
48 | Set the Pie/Doughnut chart rotation: the angle of the first slice.
49 |
50 | Args:
51 | rotation: First segment angle: 0 <= rotation <= 360.
52 |
53 | Returns:
54 | Nothing.
55 |
56 | """
57 | if rotation is None:
58 | return
59 |
60 | # Ensure the rotation is in Excel's range.
61 | if rotation < 0 or rotation > 360:
62 | warn("Chart rotation %d outside Excel range: 0 <= rotation <= 360"
63 | % rotation)
64 | return
65 |
66 | self.rotation = int(rotation)
67 |
68 | ###########################################################################
69 | #
70 | # Private API.
71 | #
72 | ###########################################################################
73 |
74 | def _write_chart_type(self, args):
75 | # Override the virtual superclass method with a chart specific method.
76 | # Write the c:pieChart element.
77 | self._write_pie_chart(args)
78 |
79 | ###########################################################################
80 | #
81 | # XML methods.
82 | #
83 | ###########################################################################
84 |
85 | def _write_pie_chart(self, args):
86 | # Write the element. Over-ridden method to remove
87 | # axis_id code since Pie charts don't require val and cat axes.
88 | self._xml_start_tag('c:pieChart')
89 |
90 | # Write the c:varyColors element.
91 | self._write_vary_colors()
92 |
93 | # Write the series elements.
94 | for data in self.series:
95 | self._write_ser(data)
96 |
97 | # Write the c:firstSliceAng element.
98 | self._write_first_slice_ang()
99 |
100 | self._xml_end_tag('c:pieChart')
101 |
102 | def _write_plot_area(self):
103 | # Over-ridden method to remove the cat_axis() and val_axis() code
104 | # since Pie charts don't require those axes.
105 | #
106 | # Write the element.
107 |
108 | self._xml_start_tag('c:plotArea')
109 |
110 | # Write the c:layout element.
111 | self._write_layout(self.plotarea.get('layout'), 'plot')
112 |
113 | # Write the subclass chart type element.
114 | self._write_chart_type(None)
115 |
116 | self._xml_end_tag('c:plotArea')
117 |
118 | def _write_legend(self):
119 | # Over-ridden method to add to legend.
120 | # Write the element.
121 |
122 | position = self.legend_position
123 | font = self.legend_font
124 | delete_series = []
125 | overlay = 0
126 |
127 | if (self.legend_delete_series is not None
128 | and type(self.legend_delete_series) is list):
129 | delete_series = self.legend_delete_series
130 |
131 | if position.startswith('overlay_'):
132 | position = position.replace('overlay_', '')
133 | overlay = 1
134 |
135 | allowed = {
136 | 'right': 'r',
137 | 'left': 'l',
138 | 'top': 't',
139 | 'bottom': 'b',
140 | }
141 |
142 | if position == 'none':
143 | return
144 |
145 | if position not in allowed:
146 | return
147 |
148 | position = allowed[position]
149 |
150 | self._xml_start_tag('c:legend')
151 |
152 | # Write the c:legendPos element.
153 | self._write_legend_pos(position)
154 |
155 | # Remove series labels from the legend.
156 | for index in delete_series:
157 | # Write the c:legendEntry element.
158 | self._write_legend_entry(index)
159 |
160 | # Write the c:layout element.
161 | self._write_layout(self.legend_layout, 'legend')
162 |
163 | # Write the c:overlay element.
164 | if overlay:
165 | self._write_overlay()
166 |
167 | # Write the c:txPr element. Over-ridden.
168 | self._write_tx_pr_legend(None, font)
169 |
170 | self._xml_end_tag('c:legend')
171 |
172 | def _write_tx_pr_legend(self, horiz, font):
173 | # Write the element for legends.
174 |
175 | if font and font.get('rotation'):
176 | rotation = font['rotation']
177 | else:
178 | rotation = None
179 |
180 | self._xml_start_tag('c:txPr')
181 |
182 | # Write the a:bodyPr element.
183 | self._write_a_body_pr(rotation, horiz)
184 |
185 | # Write the a:lstStyle element.
186 | self._write_a_lst_style()
187 |
188 | # Write the a:p element.
189 | self._write_a_p_legend(font)
190 |
191 | self._xml_end_tag('c:txPr')
192 |
193 | def _write_a_p_legend(self, font):
194 | # Write the element for legends.
195 |
196 | self._xml_start_tag('a:p')
197 |
198 | # Write the a:pPr element.
199 | self._write_a_p_pr_legend(font)
200 |
201 | # Write the a:endParaRPr element.
202 | self._write_a_end_para_rpr()
203 |
204 | self._xml_end_tag('a:p')
205 |
206 | def _write_a_p_pr_legend(self, font):
207 | # Write the element for legends.
208 | attributes = [('rtl', 0)]
209 |
210 | self._xml_start_tag('a:pPr', attributes)
211 |
212 | # Write the a:defRPr element.
213 | self._write_a_def_rpr(font)
214 |
215 | self._xml_end_tag('a:pPr')
216 |
217 | def _write_vary_colors(self):
218 | # Write the element.
219 | attributes = [('val', 1)]
220 |
221 | self._xml_empty_tag('c:varyColors', attributes)
222 |
223 | def _write_first_slice_ang(self):
224 | # Write the element.
225 | attributes = [('val', self.rotation)]
226 |
227 | self._xml_empty_tag('c:firstSliceAng', attributes)
228 |
--------------------------------------------------------------------------------
/app/templates/main/index_page.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | iOS私有api检查
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
18 |
19 |
20 |
21 |
31 |
32 |
33 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
--------------------------------------------------------------------------------
/utils/lib/xlsxwriter/contenttypes.py:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | #
3 | # ContentTypes - A class for writing the Excel XLSX ContentTypes file.
4 | #
5 | # Copyright 2013-2015, John McNamara, jmcnamara@cpan.org
6 | #
7 |
8 | import copy
9 | from . import xmlwriter
10 |
11 | # Long namespace strings used in the class.
12 | app_package = 'application/vnd.openxmlformats-package.'
13 | app_document = 'application/vnd.openxmlformats-officedocument.'
14 |
15 | defaults = [
16 | ['rels', app_package + 'relationships+xml'],
17 | ['xml', 'application/xml'],
18 | ]
19 |
20 | overrides = [
21 | ['/docProps/app.xml', app_document + 'extended-properties+xml'],
22 | ['/docProps/core.xml', app_package + 'core-properties+xml'],
23 | ['/xl/styles.xml', app_document + 'spreadsheetml.styles+xml'],
24 | ['/xl/theme/theme1.xml', app_document + 'theme+xml'],
25 | ['/xl/workbook.xml', app_document + 'spreadsheetml.sheet.main+xml'],
26 | ]
27 |
28 |
29 | class ContentTypes(xmlwriter.XMLwriter):
30 | """
31 | A class for writing the Excel XLSX ContentTypes file.
32 |
33 |
34 | """
35 |
36 | ###########################################################################
37 | #
38 | # Public API.
39 | #
40 | ###########################################################################
41 |
42 | def __init__(self):
43 | """
44 | Constructor.
45 |
46 | """
47 |
48 | super(ContentTypes, self).__init__()
49 |
50 | # Copy the defaults in case we need to change them.
51 | self.defaults = copy.deepcopy(defaults)
52 | self.overrides = copy.deepcopy(overrides)
53 |
54 | ###########################################################################
55 | #
56 | # Private API.
57 | #
58 | ###########################################################################
59 |
60 | def _assemble_xml_file(self):
61 | # Assemble and write the XML file.
62 |
63 | # Write the XML declaration.
64 | self._xml_declaration()
65 |
66 | self._write_types()
67 | self._write_defaults()
68 | self._write_overrides()
69 |
70 | self._xml_end_tag('Types')
71 |
72 | # Close the file.
73 | self._xml_close()
74 |
75 | def _add_default(self, default):
76 | # Add elements to the ContentTypes defaults.
77 | self.defaults.append(default)
78 |
79 | def _add_override(self, override):
80 | # Add elements to the ContentTypes overrides.
81 | self.overrides.append(override)
82 |
83 | def _add_worksheet_name(self, worksheet_name):
84 | # Add the name of a worksheet to the ContentTypes overrides.
85 | worksheet_name = "/xl/worksheets/" + worksheet_name + ".xml"
86 |
87 | self._add_override((worksheet_name,
88 | app_document + 'spreadsheetml.worksheet+xml'))
89 |
90 | def _add_chartsheet_name(self, chartsheet_name):
91 | # Add the name of a chartsheet to the ContentTypes overrides.
92 | chartsheet_name = "/xl/chartsheets/" + chartsheet_name + ".xml"
93 |
94 | self._add_override((chartsheet_name,
95 | app_document + 'spreadsheetml.chartsheet+xml'))
96 |
97 | def _add_chart_name(self, chart_name):
98 | # Add the name of a chart to the ContentTypes overrides.
99 | chart_name = "/xl/charts/" + chart_name + ".xml"
100 |
101 | self._add_override((chart_name, app_document + 'drawingml.chart+xml'))
102 |
103 | def _add_drawing_name(self, drawing_name):
104 | # Add the name of a drawing to the ContentTypes overrides.
105 | drawing_name = "/xl/drawings/" + drawing_name + ".xml"
106 |
107 | self._add_override((drawing_name, app_document + 'drawing+xml'))
108 |
109 | def _add_vml_name(self):
110 | # Add the name of a VML drawing to the ContentTypes defaults.
111 | self._add_default(('vml', app_document + 'vmlDrawing'))
112 |
113 | def _add_comment_name(self, comment_name):
114 | # Add the name of a comment to the ContentTypes overrides.
115 | comment_name = "/xl/" + comment_name + ".xml"
116 |
117 | self._add_override((comment_name,
118 | app_document + 'spreadsheetml.comments+xml'))
119 |
120 | def _add_shared_strings(self):
121 | # Add the sharedStrings link to the ContentTypes overrides.
122 | self._add_override(('/xl/sharedStrings.xml',
123 | app_document + 'spreadsheetml.sharedStrings+xml'))
124 |
125 | def _add_calc_chain(self):
126 | # Add the calcChain link to the ContentTypes overrides.
127 | self._add_override(('/xl/calcChain.xml',
128 | app_document + 'spreadsheetml.calcChain+xml'))
129 |
130 | def _add_image_types(self, image_types):
131 | # Add the image default types.
132 | for image_type in image_types:
133 | self._add_default((image_type, 'image/' + image_type))
134 |
135 | def _add_table_name(self, table_name):
136 | # Add the name of a table to the ContentTypes overrides.
137 | table_name = "/xl/tables/" + table_name + ".xml"
138 |
139 | self._add_override((table_name,
140 | app_document + 'spreadsheetml.table+xml'))
141 |
142 | def _add_vba_project(self):
143 | # Add a vbaProject to the ContentTypes defaults.
144 |
145 | # Change the workbook.xml content-type from xlsx to xlsm.
146 | for i, override in enumerate(self.overrides):
147 | if override[0] == '/xl/workbook.xml':
148 | self.overrides[i][1] = 'application/vnd.ms-excel.' \
149 | 'sheet.macroEnabled.main+xml'
150 |
151 | self._add_default(('bin', 'application/vnd.ms-office.vbaProject'))
152 |
153 | ###########################################################################
154 | #
155 | # XML methods.
156 | #
157 | ###########################################################################
158 |
159 | def _write_defaults(self):
160 | # Write out all of the types.
161 |
162 | for extension, content_type in self.defaults:
163 | self._xml_empty_tag('Default',
164 | [('Extension', extension),
165 | ('ContentType', content_type)])
166 |
167 | def _write_overrides(self):
168 | # Write out all of the types.
169 | for part_name, content_type in self.overrides:
170 | self._xml_empty_tag('Override',
171 | [('PartName', part_name),
172 | ('ContentType', content_type)])
173 |
174 | def _write_types(self):
175 | # Write the element.
176 | xmlns = 'http://schemas.openxmlformats.org/package/2006/content-types'
177 |
178 | attributes = [('xmlns', xmlns,)]
179 | self._xml_start_tag('Types', attributes)
180 |
181 | def _write_default(self, extension, content_type):
182 | # Write the element.
183 | attributes = [
184 | ('Extension', extension),
185 | ('ContentType', content_type),
186 | ]
187 |
188 | self._xml_empty_tag('Default', attributes)
189 |
190 | def _write_override(self, part_name, content_type):
191 | # Write the element.
192 | attributes = [
193 | ('PartName', part_name),
194 | ('ContentType', content_type),
195 | ]
196 |
197 | self._xml_empty_tag('Override', attributes)
198 |
--------------------------------------------------------------------------------
/utils/lib/xlsxwriter/xmlwriter.py:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | #
3 | # XMLwriter - A base class for XlsxWriter classes.
4 | #
5 | # Used in conjunction with XlsxWriter.
6 | #
7 | # Copyright 2013-2015, John McNamara, jmcnamara@cpan.org
8 | #
9 |
10 | # Standard packages.
11 | import re
12 | import codecs
13 |
14 | # Standard packages in Python 2/3 compatibility mode.
15 | from .compatibility import StringIO
16 |
17 |
18 | class XMLwriter(object):
19 | """
20 | Simple XML writer class.
21 |
22 | """
23 |
24 | def __init__(self):
25 | self.fh = None
26 | self.escapes = re.compile('["&<>]')
27 | self.internal_fh = False
28 |
29 | def _set_filehandle(self, filehandle):
30 | # Set the writer filehandle directly. Mainly for testing.
31 | self.fh = filehandle
32 | self.internal_fh = False
33 |
34 | def _set_xml_writer(self, filename):
35 | # Set the XML writer filehandle for the object.
36 | if isinstance(filename, StringIO):
37 | self.internal_fh = False
38 | self.fh = filename
39 | else:
40 | self.internal_fh = True
41 | self.fh = codecs.open(filename, 'w', 'utf-8')
42 |
43 | def _xml_close(self):
44 | # Close the XML filehandle if we created it.
45 | if self.internal_fh:
46 | self.fh.close()
47 |
48 | def _xml_declaration(self):
49 | # Write the XML declaration.
50 | self.fh.write(
51 | """\n""")
52 |
53 | def _xml_start_tag(self, tag, attributes=[]):
54 | # Write an XML start tag with optional attributes.
55 | for key, value in attributes:
56 | value = self._escape_attributes(value)
57 | tag += ' %s="%s"' % (key, value)
58 |
59 | self.fh.write("<%s>" % tag)
60 |
61 | def _xml_start_tag_unencoded(self, tag, attributes=[]):
62 | # Write an XML start tag with optional, unencoded, attributes.
63 | # This is a minor speed optimisation for elements that don't
64 | # need encoding.
65 | for key, value in attributes:
66 | tag += ' %s="%s"' % (key, value)
67 |
68 | self.fh.write("<%s>" % tag)
69 |
70 | def _xml_end_tag(self, tag):
71 | # Write an XML end tag.
72 | self.fh.write("%s>" % tag)
73 |
74 | def _xml_empty_tag(self, tag, attributes=[]):
75 | # Write an empty XML tag with optional attributes.
76 | for key, value in attributes:
77 | value = self._escape_attributes(value)
78 | tag += ' %s="%s"' % (key, value)
79 |
80 | self.fh.write("<%s/>" % tag)
81 |
82 | def _xml_empty_tag_unencoded(self, tag, attributes=[]):
83 | # Write an empty XML tag with optional, unencoded, attributes.
84 | # This is a minor speed optimisation for elements that don't
85 | # need encoding.
86 | for key, value in attributes:
87 | tag += ' %s="%s"' % (key, value)
88 |
89 | self.fh.write("<%s/>" % tag)
90 |
91 | def _xml_data_element(self, tag, data, attributes=[]):
92 | # Write an XML element containing data with optional attributes.
93 | end_tag = tag
94 |
95 | for key, value in attributes:
96 | value = self._escape_attributes(value)
97 | tag += ' %s="%s"' % (key, value)
98 |
99 | data = self._escape_data(data)
100 | self.fh.write("<%s>%s%s>" % (tag, data, end_tag))
101 |
102 | def _xml_string_element(self, index, attributes=[]):
103 | # Optimised tag writer for cell string elements in the inner loop.
104 | attr = ''
105 |
106 | for key, value in attributes:
107 | value = self._escape_attributes(value)
108 | attr += ' %s="%s"' % (key, value)
109 |
110 | self.fh.write("""%d""" % (attr, index))
111 |
112 | def _xml_si_element(self, string, attributes=[]):
113 | # Optimised tag writer for shared strings elements.
114 | attr = ''
115 |
116 | for key, value in attributes:
117 | value = self._escape_attributes(value)
118 | attr += ' %s="%s"' % (key, value)
119 |
120 | string = self._escape_data(string)
121 |
122 | self.fh.write("""%s""" % (attr, string))
123 |
124 | def _xml_rich_si_element(self, string):
125 | # Optimised tag writer for shared strings rich string elements.
126 |
127 | self.fh.write("""%s""" % string)
128 |
129 | def _xml_number_element(self, number, attributes=[]):
130 | # Optimised tag writer for cell number elements in the inner loop.
131 | attr = ''
132 |
133 | for key, value in attributes:
134 | value = self._escape_attributes(value)
135 | attr += ' %s="%s"' % (key, value)
136 |
137 | self.fh.write("""%.15g""" % (attr, number))
138 |
139 | def _xml_formula_element(self, formula, result, attributes=[]):
140 | # Optimised tag writer for cell formula elements in the inner loop.
141 | attr = ''
142 |
143 | for key, value in attributes:
144 | value = self._escape_attributes(value)
145 | attr += ' %s="%s"' % (key, value)
146 |
147 | self.fh.write("""%s%s"""
148 | % (attr, self._escape_data(formula),
149 | self._escape_data(result)))
150 |
151 | def _xml_inline_string(self, string, preserve, attributes=[]):
152 | # Optimised tag writer for inlineStr cell elements in the inner loop.
153 | attr = ''
154 | t_attr = ''
155 |
156 | # Set the attribute to preserve whitespace.
157 | if preserve:
158 | t_attr = ' xml:space="preserve"'
159 |
160 | for key, value in attributes:
161 | value = self._escape_attributes(value)
162 | attr += ' %s="%s"' % (key, value)
163 |
164 | string = self._escape_data(string)
165 |
166 | self.fh.write("""%s""" %
167 | (attr, t_attr, string))
168 |
169 | def _xml_rich_inline_string(self, string, attributes=[]):
170 | # Optimised tag writer for rich inlineStr in the inner loop.
171 | attr = ''
172 |
173 | for key, value in attributes:
174 | value = self._escape_attributes(value)
175 | attr += ' %s="%s"' % (key, value)
176 |
177 | self.fh.write("""%s""" %
178 | (attr, string))
179 |
180 | def _escape_attributes(self, attribute):
181 | # Escape XML characters in attributes.
182 | try:
183 | if not self.escapes.search(attribute):
184 | return attribute
185 | except TypeError:
186 | return attribute
187 |
188 | attribute = attribute.replace('&', '&')
189 | attribute = attribute.replace('"', '"')
190 | attribute = attribute.replace('<', '<')
191 | attribute = attribute.replace('>', '>')
192 |
193 | return attribute
194 |
195 | def _escape_data(self, data):
196 | # Escape XML characters in data sections of tags. Note, this
197 | # is different from _escape_attributes() in that double quotes
198 | # are not escaped by Excel.
199 | try:
200 | if not self.escapes.search(data):
201 | return data
202 | except TypeError:
203 | return data
204 |
205 | data = data.replace('&', '&')
206 | data = data.replace('<', '<')
207 | data = data.replace('>', '>')
208 |
209 | return data
210 |
--------------------------------------------------------------------------------
/iOS_private.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年10月27日
4 | iOS private api检查入口
5 | @author: atool
6 | '''
7 | import os, shutil
8 | from utils import report_utils, utils
9 | from dump import otool_utils, codesign_utils
10 | from api import app_utils, api_utils
11 | from db import api_dbs
12 | # from app.utils import IpaParse
13 | from app.utils import checkipa
14 |
15 | def get_executable_path(ipa_path, pid):
16 | '''
17 | info: unzip ipa, get execute app path
18 | '''
19 | if not os.path.exists(ipa_path):
20 | #不存在,返回检查结果为空值
21 | return False
22 | cur_dir = os.getcwd()
23 | dest = os.path.join(cur_dir, 'tmp/' + pid)
24 | if not os.path.exists(dest):
25 | os.mkdir(dest)
26 | app_path = app_utils.unzip_ipa(ipa_path, dest) #解压ipa,获得xxx.app目录路径
27 | app = app_utils.get_executable_file(app_path)
28 |
29 | return app
30 |
31 | #检查私有api,返回三个参数
32 | def check_private_api(app, pid):
33 | strings = app_utils.get_app_strings(app, pid) #一般是app中的一些可打印文本
34 | #app中的私有库和公有库 .framework
35 | private, public = otool_utils.otool_app(app)
36 | print '=' * 15
37 | print 'private:', len(private)
38 | print 'public', len(public)
39 | print '=' * 15
40 |
41 | dump_result = app_utils.get_dump_result(app)
42 | app_varibles = app_utils.get_app_variables(dump_result, pid) #app自定义的一些方法,不需要检查
43 | left = strings - app_varibles #去除一些app中开发人员自定义的方法,剩余app中的一些字符串
44 |
45 | app_methods = app_utils.get_app_methods(dump_result, pid) #dump-class分析出app中的类和方法名
46 |
47 | app_apis = []
48 | for m in app_methods:
49 | class_name = m["class"] if m["class"] != "ctype" else 'cur_app'
50 | #if m["class"] != "ctype" else 'cur_app'
51 | method_list = m["methods"]
52 | m_type = m["type"]
53 | for m in method_list:
54 | tmp_api = {}
55 | tmp_api['api_name'] = m
56 | tmp_api['class_name'] = class_name
57 | tmp_api['type'] = m_type
58 | #tmp_api['header_file'] = ''
59 | #tmp_api['sdk'] = ''
60 | #tmp_api['framework'] = ''
61 | app_apis.append(tmp_api)
62 |
63 | api_set = api_dbs.get_private_api_list(public) #数据库中的私有api,去除了whitelist白名单
64 | print '=' * 15
65 | print 'left app_varibles:', len(left)
66 | print 'app_methods:', len(app_apis)
67 | print 'private length:', len(api_set)
68 | print '=' * 15
69 | inter_api = api_utils.intersection_list_and_api(left, api_set) # app中的api和数据库中的私有api取交集,获得app中的私有api关键字数据
70 |
71 | methods_in_app, method_not_in = api_utils.intersection_api(app_apis, inter_api) #app中的私有方法
72 |
73 | print '=' * 15
74 | print 'methods_in_app', len(methods_in_app)
75 | print 'methods_not_in_app', len(method_not_in)
76 | # for i in xrange(20):
77 | # print methods_not_in_app[i]
78 | print '=' * 15
79 |
80 |
81 | return methods_in_app, method_not_in, private
82 |
83 | #检查架构,返回架构数组
84 | def check_architectures(app):
85 | arcs = app_utils.check_architectures(app)
86 | return arcs
87 |
88 | #检查xcode ghost,返回bool
89 | def check_xcode_ghost(app):
90 | return app_utils.check_xcode_ghost(app)
91 |
92 | #检查info和provision文件,并获取建议和错误配置
93 | def check_app_info_and_provision(ipa):
94 | return checkipa.process_ipa(ipa)
95 |
96 | #检查codesign信息
97 | def check_codesign(app):
98 | return codesign_utils.codesignapp(app)
99 |
100 | #ipa的md5
101 | def get_file_md5(ipa):
102 | return app_utils.file_md5(ipa)
103 |
104 | #检查单个的app,获得结果字典
105 | def check_ipa(ipa_path):
106 | result = {} #每个app的检查结果
107 | pid = utils.get_unique_str()
108 | print '1.', '+' * 10, 'get_file_md5'
109 | result['md5'] = get_file_md5(ipa_path)
110 |
111 | print '2.', '+' * 10, 'check_app_info_and_provision'
112 | rsts = check_app_info_and_provision(ipa_path)
113 | for key in rsts.keys():
114 | result[key] = rsts[key]
115 | #检查ios私有api
116 | print '3.', '+' * 10, 'check_private_api'
117 | app = get_executable_path(ipa_path, pid)
118 | if not app:
119 | #找不到math-o文件,说明不是正常的ipa,忽略
120 | return False
121 |
122 | methods_in_app, _, private = check_private_api(app, pid)
123 | result['private_apis'] = methods_in_app
124 | # result['private_apis_not'] = _
125 | result['private_frameworks'] = list(private)
126 | #检查ipa 64支持情况
127 | print '4.', '+' * 10, 'check_architectures'
128 | arcs = check_architectures(app)
129 | result['arcs'] = arcs
130 | if len(arcs) < 2:
131 | result['error'].append({'label': 'Architecture:',
132 | 'description': 'app may be not support 64-bit'})
133 | #检查ghost情况
134 | print '5.', '+' * 10, 'check_xcode_ghost'
135 | ghost = check_xcode_ghost(app)
136 | result['ghost'] = ghost
137 | #检查codesign
138 | print '6.', '+' * 10, 'check_private_api'
139 | codesign = check_codesign(app)
140 | result['codesign'] = codesign
141 |
142 | print '7.', '+' * 10, 'remove tmp files'
143 | cur_dir = os.getcwd() #删除检查临时目录
144 | dest_tmp = os.path.join(cur_dir, 'tmp/' + pid)
145 | # print 'tmp:', dest_tmp
146 | if os.path.exists(dest_tmp):
147 | shutil.rmtree(dest_tmp)
148 |
149 | return result
150 |
151 | def batch_check(app_folder, excel_path):
152 | '''
153 | 批量检测多个ipa,并产生excel报告
154 | '''
155 | #遍历folder,找出.ipa文件
156 | if not app_folder or not excel_path:
157 | return False
158 |
159 | check_results = []
160 | ipa_list = os.listdir(app_folder)
161 | for ipa in ipa_list:
162 | print 'start check :', ipa
163 | if ipa.endswith('.ipa'):
164 | ipa_path = os.path.join(app_folder, ipa)
165 | #单个app的检查结果
166 | try:
167 | r = check_ipa(ipa_path)
168 | if r:
169 | check_results.append(r)
170 | except Exception, e:
171 | print e
172 | continue
173 |
174 | #将结果转化成excel报告
175 | report_utils.excel_report(check_results, excel_path)
176 | return excel_path
177 |
178 | if __name__ == '__main__':
179 | #######
180 | #check one app
181 | # ipa_path = "/Users/summer-wj/code/svn/ljsg_for_netease_20150928_resign.ipa"
182 |
183 | # private_1 = open("tmp/private_1.txt", "w")
184 | # private_2 = open("tmp/private_2.txt", "w")
185 | # #将strings内容输出到文件中
186 | # pid = app_utils.get_unique_str()
187 | # app = get_executable_path(ipa_path, pid)
188 | # print app
189 | # arcs = check_architectures(app)
190 | # print arcs
191 | # a, b, c = check_private_api(app, pid)
192 | # print "=" * 50
193 | # print len(a), "Private Methods in App:"
194 | # print "*" * 50
195 | # for aa in a:
196 | # print aa
197 | # print >>private_1, aa
198 |
199 | # print "=" * 50
200 | # print len(b), "Private Methods not in App, May in Framework Used:"
201 | # print "*" * 50
202 | # for bb in b:
203 | # print >>private_2, bb
204 |
205 | # print "=" * 50
206 | # print len(c), "Private Framework in App:"
207 | # print "*" * 50
208 |
209 | ##########
210 | #test batch check ipa
211 | cwd = os.getcwd()
212 | excel_path = os.path.join(cwd, 'tmp/' + utils.get_unique_str() + '.xlsx')
213 | # excel_path = os.path.join(cwd, 'tmp/test.xlsx') # for test
214 | print excel_path
215 | ipa_folder = '/Users/netease/Downloads/ipas/mg/'
216 | # ipa_folder = '/Users/netease/Music/iTunes/iTunes Media/Mobile Applications/'
217 | # ipa_folder = '/Users/netease/Music/iTunes/iTunes Media/'
218 | print batch_check(ipa_folder, excel_path)
219 |
220 | #########
221 | #test check arcs
222 | # app_path = '/Users/netease/Downloads/ipas/mg/Payload'
223 | # app = app_utils.get_executable_file(app_path)
224 | # print check_architectures(app)
225 |
226 |
--------------------------------------------------------------------------------
/api/api_utils.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | '''
3 | Created on 2015年10月27日
4 | 各种api的获取方式,最后通过这些api计算出私有api的集合,用来后续计算app中是否使用私有api
5 | @author: atool
6 | '''
7 | from db import dsidx_dbs
8 | import os
9 | import api
10 | from api import api_helpers
11 | from dump import class_dump_utils
12 | from itertools import groupby
13 |
14 | def framework_dump_apis(sdk, framework_folder):
15 | '''
16 | class-dump Framework下的库生成的头文件中的api
17 | sdk: sdk version
18 | info: 用class-dump对所有的公开库(/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.0.sdk/System/Library/Frameworks)进行逆向工程得到所有的头文件内容。提取每个.h文件中的api得到api集合set_A。
19 | '''
20 |
21 | #分析frame,将头文件输出到tmp/pub-headers目录
22 | framework_header_folder = _dump_frameworks(framework_folder, 'pub-headers')
23 |
24 | #得到.h文件
25 | all_headers = _get_headers_from_path(framework_header_folder)
26 | #解析文件内容,获得api
27 | framework_apis = _get_apis_from_headers(sdk, all_headers)
28 |
29 | return framework_apis
30 |
31 |
32 | def framework_header_apis(sdk, framework_folder):
33 | '''
34 | get all public frameworks' header files(documented)
35 | '''
36 | all_headers = _get_headers_from_path(framework_folder)
37 |
38 | framework_apis = _get_apis_from_headers(sdk, all_headers)
39 |
40 | return framework_apis
41 |
42 | #没有文档的api
43 | def undocument_apis(sdk):
44 | '''
45 | set_C = set_header - set_B
46 | info:不在文档中的api方法
47 | '''
48 | framework_header_apis(sdk) - document_apis(sdk)
49 |
50 | #有文档的api
51 | def document_apis(sdk, db_path):
52 | '''
53 | has document apis
54 | info:获得带文档的api。(/Users/Test/Library/Developer/Shared/Documentation/DocSets/com.apple.adc.documentation.AppleiOS7.0.iOSLibrary.docset/Contents/Resources),在里面有个docSet.dsidx的文件,这就是Xcode针对api做的数据库,从这里可以获得带文档的api的各种信息了,从而有了带文档的api集合set_B。
55 |
56 | '''
57 | doc_apis = []
58 | #从dsidx 数据库中获得初始数据
59 | apiset = dsidx_dbs.get_dsidx_apis(db_path)
60 | #过滤初始数据获得有文档的api集合
61 | for api in apiset:
62 | Z_PK = api['Z_PK']
63 | ZDECLAREDIN = api['ZDECLAREDIN']
64 | ZCONTAINER = api['ZCONTAINER']
65 | # get containername from ZCONTAINER table
66 | container_name = ''
67 | if Z_PK:
68 | container_name = dsidx_dbs.get_container_name(ZCONTAINER, db_path) or ''
69 | # get frameworkname and headerpath from ZHEADER table
70 |
71 | framework_name = ''
72 | header_path = ''
73 | if ZDECLAREDIN:
74 | frame_header = dsidx_dbs.get_framework_and_header(ZDECLAREDIN, db_path)
75 | if frame_header:
76 | framework_name = frame_header.get('ZFRAMEWORKNAME', '')
77 | header_path = frame_header.get('ZHEADERPATH', '')
78 |
79 | doc_apis.append({'api_name': api['ZTOKENNAME'], 'class_name': container_name, 'type': api['ZTOKENTYPE'], 'header_file': header_path, 'framework': framework_name, 'sdk': sdk})
80 | return doc_apis
81 |
82 | def private_framework_dump_apis(sdk, framework_folder):
83 | '''
84 | PrivateFramework下的api
85 | '''
86 | framework_header_folder = _dump_frameworks(framework_folder, 'pri-headers')
87 | #得到.h文件
88 | all_headers = _get_headers_from_path(framework_header_folder)
89 | #解析文件内容,获得api
90 | framework_apis = _get_apis_from_headers(sdk, all_headers)
91 |
92 | return framework_apis
93 |
94 | def all_private_apis(sdk, include_private_framework = False):
95 | '''
96 | info: 私有的api = (
97 | class-dump Framework下的库生成的头文件中的api
98 | -
99 | (Framework下的头文件里的api = 有文档的api + 没有文档的api)
100 | )
101 | +
102 | PrivateFramework下的api。
103 | '''
104 | pub_private_apis = framework_dump_apis(sdk) - framework_header_apis(sdk)
105 |
106 | if include_private_framework:
107 | pri_private_apis = private_framework_apis(sdk)
108 |
109 | return pub_private_apis + pri_private_apis
110 | return pub_private_apis
111 |
112 |
113 | #目录迭代器
114 | def iterate_dir(framework, prefix, path):
115 | files = []
116 | for f in os.listdir(path):
117 | if os.path.isfile(os.path.join(path, f)):
118 | files.append((framework, prefix + f, os.path.join(path, f)))
119 | elif os.path.isdir(os.path.join(path, f)):
120 | files += iterate_dir(framework, prefix + f + "/", os.path.join(path, f))
121 | return files
122 |
123 | #从framework目录,获得所有的.h文件
124 | def _get_headers_from_path(framework_folder):
125 | all_headers_path = []
126 |
127 | frameworks = os.listdir(framework_folder)
128 | #print frameworks
129 | for framework in frameworks:
130 | if framework.endswith(".framework"):
131 | header_path = os.path.join(os.path.join(framework_folder, framework), 'Headers')
132 | if os.path.exists(header_path):
133 | all_headers_path += iterate_dir(framework, "", os.path.join(framework_folder, header_path))
134 |
135 | return all_headers_path
136 |
137 |
138 | def _get_apis_from_headers(sdk, all_headers):
139 | framework_apis = []
140 | for header in all_headers:
141 | #get apis from .h file
142 | apis = api_helpers.get_apis_of_file(header[2])
143 |
144 | for api in apis:
145 | class_name = api["class"] if api["class"] != "ctype" else header[1]
146 | method_list = api["methods"]
147 | m_type = api["type"]
148 | for m in method_list:
149 | tmp_api = {}
150 | tmp_api['api_name'] = m
151 | tmp_api['class_name'] = class_name
152 | tmp_api['type'] = m_type
153 | tmp_api['header_file'] = header[1]
154 | tmp_api['sdk'] = sdk
155 | tmp_api['framework'] = header[0]
156 | framework_apis.append(tmp_api)
157 |
158 | return framework_apis
159 |
160 | #使用calss-dump分析framework目录,将.h文件输出到对应的目录
161 | def _dump_frameworks(framework_folder, prefix):
162 | cur_dir = os.getcwd()
163 | headers_path = os.path.join(cur_dir, "tmp/" + prefix)
164 |
165 | #讲frame dump到./tmp/pub_headers目录中
166 | for framework in os.listdir(framework_folder):
167 | if framework.endswith(".framework"):
168 | frame_path = os.path.join(framework_folder, framework)
169 | out_path = os.path.join(headers_path, framework)
170 | out_path = os.path.join(out_path, 'Headers') #构造目录结果: /tmp/xxx.framework/Headers/xx.h
171 | class_dump_utils.dump_framework(frame_path, out_path)
172 | return headers_path
173 |
174 |
175 | #api去重
176 | def deduplication_api_list(apis):
177 |
178 | def api_gourpby(api):
179 | return api['api_name'] + '/' + api['class_name']
180 |
181 | new_apis = []
182 |
183 | apis = sorted(apis, key = api_gourpby)
184 |
185 | for g, l in groupby(apis, key = api_gourpby):
186 | l = list(l)
187 | if l and len(l) > 0:
188 | new_apis.append(l[0])
189 |
190 | return new_apis
191 |
192 |
193 | def _apis_2_dict(apis):
194 | apis_dict = {}
195 | if apis:
196 | for api in apis:
197 | api_hash = api['api_name'] + '/' + api['class_name']
198 | apis_dict[api_hash] = api
199 |
200 | return apis_dict
201 |
202 | def intersection_api(apis_1, apis_2):
203 | '''
204 | return intersection of apis_1 and apis_2
205 | in apis_1, also in apis_2
206 | '''
207 | not_in_apis = []
208 | apis = []
209 | apis_1_dict = _apis_2_dict(apis_1)
210 |
211 | for api in apis_2:
212 | api_hash = api['api_name'] + '/' + api['class_name']
213 | if apis_1_dict.get(api_hash, None):
214 | apis.append(api)
215 | else:
216 | not_in_apis.append(api)
217 |
218 | return apis, not_in_apis
219 |
220 | def intersection_list_and_api(l, apis):
221 | '''
222 | return intersection of api_name list and api dict list
223 | '''
224 | #def _apis_2_api_dict(apis):
225 | new_apis = []
226 | #new_methods = set()
227 | #apis_dict = _apis_2_dict(apis)
228 |
229 | for api_tmp in apis:
230 | api_hash = api_tmp['api_name']
231 | if api_hash in l:
232 | new_apis.append(api_tmp)
233 |
234 | return new_apis
235 |
--------------------------------------------------------------------------------
/utils/lib/xlsxwriter/compat_collections.py:
--------------------------------------------------------------------------------
1 | """
2 | From the GC3Pie project: https://code.google.com/p/gc3pie/
3 |
4 | A backport of the Python standard `collections` package, providing
5 | `namedtuple` and `defaultdict` also on Python 2.4 and 2.5.
6 |
7 | This package actually imports your Python `collections`, and adds
8 | its own versions of `namedtuple` and `defaultdict` only if they are
9 | missing.
10 | """
11 |
12 | from collections import *
13 | import sys
14 |
15 | try:
16 | defaultdict
17 | except NameError:
18 | class defaultdict(dict):
19 | """
20 | A backport of `defaultdict` to Python 2.4
21 | See http://docs.python.org/library/collections.html
22 | """
23 | def __new__(cls, default_factory=None):
24 | return dict.__new__(cls)
25 |
26 | def __init__(self, default_factory):
27 | self.default_factory = default_factory
28 |
29 | def __missing__(self, key):
30 | try:
31 | return self.default_factory()
32 | except:
33 | raise KeyError("Key '%s' not in dictionary" % key)
34 |
35 | def __getitem__(self, key):
36 | if not dict.__contains__(self, key):
37 | dict.__setitem__(self, key, self.__missing__(key))
38 | return dict.__getitem__(self, key)
39 |
40 |
41 | try:
42 | namedtuple
43 | except NameError:
44 | # Use Raymond Hettinger's original `namedtuple` package.
45 | #
46 | # Source originally taken from:
47 | # http://code.activestate.com/recipes/500261-named-tuples/
48 | from operator import itemgetter as _itemgetter
49 | from keyword import iskeyword as _iskeyword
50 | import sys as _sys
51 |
52 | def namedtuple(typename, field_names, verbose=False, rename=False):
53 | """Returns a new subclass of tuple with named fields.
54 |
55 | >>> Point = namedtuple('Point', 'x y')
56 | >>> Point.__doc__ # docstring for the new class
57 | 'Point(x, y)'
58 | >>> p = Point(11, y=22) # instantiate with positional args or keywords
59 | >>> p[0] + p[1] # indexable like a plain tuple
60 | 33
61 | >>> x, y = p # unpack like a regular tuple
62 | >>> x, y
63 | (11, 22)
64 | >>> p.x + p.y # fields also accessable by name
65 | 33
66 | >>> d = p._asdict() # convert to a dictionary
67 | >>> d['x']
68 | 11
69 | >>> Point(**d) # convert from a dictionary
70 | Point(x=11, y=22)
71 | >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields
72 | Point(x=100, y=22)
73 |
74 | """
75 |
76 | # Parse and validate the field names. Validation serves two purposes,
77 | # generating informative error messages and preventing template injection attacks.
78 | if isinstance(field_names, basestring):
79 | field_names = field_names.replace(',', ' ').split() # names separated by whitespace and/or commas
80 | field_names = tuple(map(str, field_names))
81 | if rename:
82 | names = list(field_names)
83 | seen = set()
84 | for i, name in enumerate(names):
85 | if (not min(c.isalnum() or c == '_' for c in name)
86 | or _iskeyword(name)
87 | or not name or name[0].isdigit()
88 | or name.startswith('_')
89 | or name in seen):
90 | names[i] = '_%d' % i
91 |
92 | seen.add(name)
93 | field_names = tuple(names)
94 | for name in (typename,) + field_names:
95 | if not min(c.isalnum() or c == '_' for c in name):
96 | raise ValueError('Type names and field names can only contain alphanumeric characters and underscores: %r' % name)
97 | if _iskeyword(name):
98 | raise ValueError('Type names and field names cannot be a keyword: %r' % name)
99 | if name[0].isdigit():
100 | raise ValueError('Type names and field names cannot start with a number: %r' % name)
101 | seen_names = set()
102 | for name in field_names:
103 | if name.startswith('_') and not rename:
104 | raise ValueError('Field names cannot start with an underscore: %r' % name)
105 | if name in seen_names:
106 | raise ValueError('Encountered duplicate field name: %r' % name)
107 | seen_names.add(name)
108 |
109 | # Create and fill-in the class template
110 | numfields = len(field_names)
111 | argtxt = repr(field_names).replace("'", "")[1:-1] # tuple repr without parens or quotes
112 | reprtxt = ', '.join('%s=%%r' % name for name in field_names)
113 | template = '''class %(typename)s(tuple):
114 | '%(typename)s(%(argtxt)s)' \n
115 | __slots__ = () \n
116 | _fields = %(field_names)r \n
117 | def __new__(_cls, %(argtxt)s):
118 | return _tuple.__new__(_cls, (%(argtxt)s)) \n
119 | @classmethod
120 | def _make(cls, iterable, new=tuple.__new__, len=len):
121 | 'Make a new %(typename)s object from a sequence or iterable'
122 | result = new(cls, iterable)
123 | if len(result) != %(numfields)d:
124 | raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result))
125 | return result \n
126 | def __repr__(self):
127 | return '%(typename)s(%(reprtxt)s)' %% self \n
128 | def _asdict(self):
129 | 'Return a new dict which maps field names to their values'
130 | return dict(zip(self._fields, self)) \n
131 | def _replace(_self, **kwds):
132 | 'Return a new %(typename)s object replacing specified fields with new values'
133 | result = _self._make(map(kwds.pop, %(field_names)r, _self))
134 | if kwds:
135 | raise ValueError('Got unexpected field names: %%r' %% kwds.keys())
136 | return result \n
137 | def __getnewargs__(self):
138 | return tuple(self) \n\n''' % locals()
139 | for i, name in enumerate(field_names):
140 | template += ' %s = _property(_itemgetter(%d))\n' % (name, i)
141 | if verbose:
142 | print(template)
143 |
144 | # Execute the template string in a temporary namespace
145 | namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename,
146 | _property=property, _tuple=tuple)
147 | try:
148 | exec(template) in namespace
149 | except SyntaxError:
150 | e = sys.exc_info()[1]
151 | raise SyntaxError(str(e) + ':\n' + template)
152 | result = namespace[typename]
153 |
154 | # For pickling to work, the __module__ variable needs to be set to the frame
155 | # where the named tuple is created. Bypass this step in enviroments where
156 | # sys._getframe is not defined (Jython for example) or sys._getframe is not
157 | # defined for arguments greater than 0 (IronPython).
158 | try:
159 | result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__')
160 | except (AttributeError, ValueError):
161 | pass
162 |
163 | return result
164 |
165 |
166 | if __name__ == '__main__':
167 | # verify that instances can be pickled
168 | from cPickle import loads, dumps
169 | Point = namedtuple('Point', 'x, y', True)
170 | p = Point(x=10, y=20)
171 | assert p == loads(dumps(p, -1))
172 |
173 | # test and demonstrate ability to override methods
174 | class Point(namedtuple('Point', 'x y')):
175 | @property
176 | def hypot(self):
177 | return (self.x ** 2 + self.y ** 2) ** 0.5
178 |
179 | def __str__(self):
180 | return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (self.x, self.y, self.hypot)
181 |
182 | for p in Point(3, 4), Point(14, 5), Point(9. / 7, 6):
183 | print(p)
184 |
185 | class Point(namedtuple('Point', 'x y')):
186 | 'Point class with optimized _make() and _replace() without error-checking'
187 | _make = classmethod(tuple.__new__)
188 |
189 | def _replace(self, _map=map, **kwds):
190 | return self._make(_map(kwds.get, ('x', 'y'), self))
191 |
192 | print(Point(11, 22)._replace(x=100))
193 |
194 | import doctest
195 | TestResults = namedtuple('TestResults', 'failed attempted')
196 | print(TestResults(*doctest.testmod()))
197 |
--------------------------------------------------------------------------------
/utils/lib/xlsxwriter/theme.py:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | #
3 | # Theme - A class for writing the Excel XLSX Worksheet file.
4 | #
5 | # Copyright 2013-2015, John McNamara, jmcnamara@cpan.org
6 | #
7 |
8 | # Standard packages.
9 | import codecs
10 | import sys
11 |
12 | # Standard packages in Python 2/3 compatibility mode.
13 | from .compatibility import StringIO
14 |
15 |
16 | class Theme(object):
17 | """
18 | A class for writing the Excel XLSX Theme file.
19 |
20 |
21 | """
22 |
23 | ###########################################################################
24 | #
25 | # Public API.
26 | #
27 | ###########################################################################
28 |
29 | def __init__(self):
30 | """
31 | Constructor.
32 |
33 | """
34 | super(Theme, self).__init__()
35 | self.fh = None
36 | self.internal_fh = False
37 |
38 | ###########################################################################
39 | #
40 | # Private API.
41 | #
42 | ###########################################################################
43 |
44 | def _assemble_xml_file(self):
45 | # Assemble and write the XML file.
46 | self._write_theme_file()
47 | if self.internal_fh:
48 | self.fh.close()
49 |
50 | def _set_xml_writer(self, filename):
51 | # Set the XML writer filehandle for the object.
52 | if isinstance(filename, StringIO):
53 | self.internal_fh = False
54 | self.fh = filename
55 | else:
56 | self.internal_fh = True
57 | self.fh = codecs.open(filename, 'w', 'utf-8')
58 |
59 | ###########################################################################
60 | #
61 | # XML methods.
62 | #
63 | ###########################################################################
64 |
65 | def _write_theme_file(self):
66 | # Write a default theme.xml file.
67 |
68 | # The theme is encoded to allow Python 2.5/Jython support.
69 | default_theme = """\n"""
70 |
71 | if sys.version_info < (3, 0, 0):
72 | default_theme = default_theme.decode('unicode-escape')
73 |
74 | self.fh.write(default_theme)
75 |
--------------------------------------------------------------------------------
/app/static/res/css/dropzone.min.css:
--------------------------------------------------------------------------------
1 | @-webkit-keyframes passing-through{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%, 70%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}100%{opacity:0;-webkit-transform:translateY(-40px);-moz-transform:translateY(-40px);-ms-transform:translateY(-40px);-o-transform:translateY(-40px);transform:translateY(-40px)}}@-moz-keyframes passing-through{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%, 70%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}100%{opacity:0;-webkit-transform:translateY(-40px);-moz-transform:translateY(-40px);-ms-transform:translateY(-40px);-o-transform:translateY(-40px);transform:translateY(-40px)}}@keyframes passing-through{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%, 70%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}100%{opacity:0;-webkit-transform:translateY(-40px);-moz-transform:translateY(-40px);-ms-transform:translateY(-40px);-o-transform:translateY(-40px);transform:translateY(-40px)}}@-webkit-keyframes slide-in{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}}@-moz-keyframes slide-in{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}}@keyframes slide-in{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}}@-webkit-keyframes pulse{0%{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}10%{-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-ms-transform:scale(1.1);-o-transform:scale(1.1);transform:scale(1.1)}20%{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}}@-moz-keyframes pulse{0%{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}10%{-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-ms-transform:scale(1.1);-o-transform:scale(1.1);transform:scale(1.1)}20%{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}}@keyframes pulse{0%{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}10%{-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-ms-transform:scale(1.1);-o-transform:scale(1.1);transform:scale(1.1)}20%{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}}.dropzone,.dropzone *{box-sizing:border-box}.dropzone{min-height:150px;border:2px solid rgba(0,0,0,0.3);background:white;padding:20px 20px}.dropzone.dz-clickable{cursor:pointer}.dropzone.dz-clickable *{cursor:default}.dropzone.dz-clickable .dz-message,.dropzone.dz-clickable .dz-message *{cursor:pointer}.dropzone.dz-started .dz-message{display:none}.dropzone.dz-drag-hover{border-style:solid}.dropzone.dz-drag-hover .dz-message{opacity:0.5}.dropzone .dz-message{text-align:center;margin:2em 0}.dropzone .dz-preview{position:relative;display:inline-block;vertical-align:top;margin:16px;min-height:100px}.dropzone .dz-preview:hover{z-index:1000}.dropzone .dz-preview:hover .dz-details{opacity:1}.dropzone .dz-preview.dz-file-preview .dz-image{border-radius:20px;background:#999;background:linear-gradient(to bottom, #eee, #ddd)}.dropzone .dz-preview.dz-file-preview .dz-details{opacity:1}.dropzone .dz-preview.dz-image-preview{background:white}.dropzone .dz-preview.dz-image-preview .dz-details{-webkit-transition:opacity 0.2s linear;-moz-transition:opacity 0.2s linear;-ms-transition:opacity 0.2s linear;-o-transition:opacity 0.2s linear;transition:opacity 0.2s linear}.dropzone .dz-preview .dz-remove{font-size:14px;text-align:center;display:block;cursor:pointer;border:none}.dropzone .dz-preview .dz-remove:hover{text-decoration:underline}.dropzone .dz-preview:hover .dz-details{opacity:1}.dropzone .dz-preview .dz-details{z-index:20;position:absolute;top:0;left:0;opacity:0;font-size:13px;min-width:100%;max-width:100%;padding:2em 1em;text-align:center;color:rgba(0,0,0,0.9);line-height:150%}.dropzone .dz-preview .dz-details .dz-size{margin-bottom:1em;font-size:16px}.dropzone .dz-preview .dz-details .dz-filename{white-space:nowrap}.dropzone .dz-preview .dz-details .dz-filename:hover span{border:1px solid rgba(200,200,200,0.8);background-color:rgba(255,255,255,0.8)}.dropzone .dz-preview .dz-details .dz-filename:not(:hover){overflow:hidden;text-overflow:ellipsis}.dropzone .dz-preview .dz-details .dz-filename:not(:hover) span{border:1px solid transparent}.dropzone .dz-preview .dz-details .dz-filename span,.dropzone .dz-preview .dz-details .dz-size span{background-color:rgba(255,255,255,0.4);padding:0 0.4em;border-radius:3px}.dropzone .dz-preview:hover .dz-image img{-webkit-transform:scale(1.05, 1.05);-moz-transform:scale(1.05, 1.05);-ms-transform:scale(1.05, 1.05);-o-transform:scale(1.05, 1.05);transform:scale(1.05, 1.05);-webkit-filter:blur(8px);filter:blur(8px)}.dropzone .dz-preview .dz-image{border-radius:20px;overflow:hidden;width:120px;height:120px;position:relative;display:block;z-index:10}.dropzone .dz-preview .dz-image img{display:block}.dropzone .dz-preview.dz-success .dz-success-mark{-webkit-animation:passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);-moz-animation:passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);-ms-animation:passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);-o-animation:passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);animation:passing-through 3s cubic-bezier(0.77, 0, 0.175, 1)}.dropzone .dz-preview.dz-error .dz-error-mark{opacity:1;-webkit-animation:slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);-moz-animation:slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);-ms-animation:slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);-o-animation:slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);animation:slide-in 3s cubic-bezier(0.77, 0, 0.175, 1)}.dropzone .dz-preview .dz-success-mark,.dropzone .dz-preview .dz-error-mark{pointer-events:none;opacity:0;z-index:500;position:absolute;display:block;top:50%;left:50%;margin-left:-27px;margin-top:-27px}.dropzone .dz-preview .dz-success-mark svg,.dropzone .dz-preview .dz-error-mark svg{display:block;width:54px;height:54px}.dropzone .dz-preview.dz-processing .dz-progress{opacity:1;-webkit-transition:all 0.2s linear;-moz-transition:all 0.2s linear;-ms-transition:all 0.2s linear;-o-transition:all 0.2s linear;transition:all 0.2s linear}.dropzone .dz-preview.dz-complete .dz-progress{opacity:0;-webkit-transition:opacity 0.4s ease-in;-moz-transition:opacity 0.4s ease-in;-ms-transition:opacity 0.4s ease-in;-o-transition:opacity 0.4s ease-in;transition:opacity 0.4s ease-in}.dropzone .dz-preview:not(.dz-processing) .dz-progress{-webkit-animation:pulse 6s ease infinite;-moz-animation:pulse 6s ease infinite;-ms-animation:pulse 6s ease infinite;-o-animation:pulse 6s ease infinite;animation:pulse 6s ease infinite}.dropzone .dz-preview .dz-progress{opacity:1;z-index:1000;pointer-events:none;position:absolute;height:16px;left:50%;top:50%;margin-top:-8px;width:80px;margin-left:-40px;background:rgba(255,255,255,0.9);-webkit-transform:scale(1);border-radius:8px;overflow:hidden}.dropzone .dz-preview .dz-progress .dz-upload{background:#333;background:linear-gradient(to bottom, #666, #444);position:absolute;top:0;left:0;bottom:0;width:0;-webkit-transition:width 300ms ease-in-out;-moz-transition:width 300ms ease-in-out;-ms-transition:width 300ms ease-in-out;-o-transition:width 300ms ease-in-out;transition:width 300ms ease-in-out}.dropzone .dz-preview.dz-error .dz-error-message{display:block}.dropzone .dz-preview.dz-error:hover .dz-error-message{opacity:1;pointer-events:auto}.dropzone .dz-preview .dz-error-message{pointer-events:none;z-index:1000;position:absolute;display:block;display:none;opacity:0;-webkit-transition:opacity 0.3s ease;-moz-transition:opacity 0.3s ease;-ms-transition:opacity 0.3s ease;-o-transition:opacity 0.3s ease;transition:opacity 0.3s ease;border-radius:8px;font-size:13px;top:130px;left:-10px;width:140px;background:#be2626;background:linear-gradient(to bottom, #be2626, #a92222);padding:0.5em 1.2em;color:white}.dropzone .dz-preview .dz-error-message:after{content:'';position:absolute;top:-6px;left:64px;width:0;height:0;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #be2626}
2 |
--------------------------------------------------------------------------------