在情人節(jié)這天寫技術(shù)文檔泵喘,太沒(méi)有情調(diào)了。不過(guò)有情人的話般妙,哪天不是情人節(jié)纪铺,哈哈。
0x00 背景
在流程不太明確碟渺,每個(gè)人的責(zé)任邊界比較模糊的時(shí)候鲜锚,軟件需求與產(chǎn)品都是個(gè)深坑。
0x001 未必是坑
對(duì)于在這個(gè)工作的經(jīng)辦人而言苫拍,這個(gè)時(shí)候的需求與產(chǎn)品會(huì)是:
- 一個(gè)關(guān)鍵的流程梳理工具
- 劃分管理責(zé)任的重要依據(jù)
- 項(xiàng)目工作流程實(shí)時(shí)可見的重要手段
- 問(wèn)題追溯定位的重要來(lái)源
0x002 要求不低
理想很豐滿芜繁,畢竟在一片混沌之中,有個(gè)人殺出一條路之后绒极,此后也就有路了骏令。所以,為了完成這個(gè)目標(biāo)集峦,系統(tǒng)應(yīng)該要具備:
- 有一個(gè)可以快速開發(fā)的框架伏社,至少包括數(shù)據(jù)持久化,后臺(tái)處理與前臺(tái)交互
- 需要一個(gè)隨需應(yīng)變(On Demand)的設(shè)計(jì)架構(gòu)塔淤,因?yàn)闃I(yè)務(wù)會(huì)隨時(shí)的改摘昌,包括原來(lái)起初為一個(gè)會(huì)議參會(huì)人員設(shè)計(jì)的功能要變成多個(gè)會(huì)議同時(shí)進(jìn)行的功能
- 開發(fā)起來(lái)時(shí)間一定要短,效果一定不能太差高蜂,而且必須通過(guò)網(wǎng)絡(luò)訪問(wèn)
嚴(yán)格來(lái)說(shuō)聪黎,上面那些需求都是非功能需求,那功能需求是:
- 能夠通過(guò)處理相片的規(guī)則化命名,新建參會(huì)人員的姓名稿饰、單位锦秒、證卡類型、參加會(huì)議的種類
- 保留那些命名錯(cuò)誤的照片喉镰,等待手工處理旅择,總有一部分人提交的數(shù)據(jù)每次都有問(wèn)題
- 整理完一個(gè)批次的入庫(kù)資料后,會(huì)打包并新建一個(gè)excel表格侣姆,經(jīng)校驗(yàn)完之后生成一個(gè)zip壓縮包生真,同時(shí)將證件的狀態(tài)變成“送打印”
- 每次打包的資料都是當(dāng)前證件類型中未打印的資料
- 卡證制作完畢后對(duì)每張證件進(jìn)行校驗(yàn),核校數(shù)據(jù)捺宗,通過(guò)點(diǎn)擊“核兄埃”按鈕,將證件狀態(tài)變成“已核醒晾鳎”
- 對(duì)成產(chǎn)完成的證卡信息自動(dòng)生成移交文檔长已,通過(guò)選擇批次數(shù)來(lái)選定數(shù)據(jù),并合并同類證卡的人員名單
- 會(huì)議結(jié)束后昼牛,整理出每種證件制作的數(shù)量术瓮,并打印名單
在功能需求中提到的工作,就是我實(shí)際工作的內(nèi)容匾嘱,數(shù)據(jù)提交很零散斤斧,要匯總成一個(gè)大表,耗費(fèi)的時(shí)間比較長(zhǎng)霎烙,沒(méi)有一個(gè)確切的統(tǒng)計(jì)數(shù)據(jù)和列表撬讽,核對(duì)制作證卡的時(shí)候就容易出錯(cuò),上面的活不是很難悬垃,就是很反鎖游昼,很容易錯(cuò),而且時(shí)候要做統(tǒng)計(jì)的時(shí)候又要花去不少的時(shí)間尝蠕,此外還可能有多次提交制卡信息的單位烘豌,一個(gè)人實(shí)在是hold不住啊。
事情做久了看彼,自己對(duì)業(yè)務(wù)熟悉了廊佩,別人就不清楚了,所以到頭來(lái)重?fù)?dān)還得自己挑靖榕。所以拿起武器标锄,開干,核心程序?qū)懥巳齻€(gè)晚上茁计,白天在真實(shí)數(shù)據(jù)與環(huán)境下調(diào)試了幾天料皇,主要是那些不按照規(guī)定亂命名的文件名,讓我寫了很多異常處理的語(yǔ)句。
0x01 準(zhǔn)備
我能拿起來(lái)的開發(fā)語(yǔ)言無(wú)非就是asp.net践剂、java鬼譬、php、python逊脯,至于javascript的什么Node.js优质,什么angularJs,什么TypeScript男窟,今年應(yīng)該就沒(méi)問(wèn)題了盆赤。備選的框架有:
- asp.net MVC5
- java Spring
- php thinkPhp
- python django/flask
為了保持技術(shù)的連貫性,我選擇python歉眷,框架的選擇是一個(gè)難題,選熟悉的django還是選更流行的flask颤枪,看了一下午的文檔之后最終選擇汗捡,挑戰(zhàn)自己,選擇從未碰過(guò)的flask畏纲。
0x011 配置環(huán)境
0x0111 選擇Anaconda
選擇Anaconda python2.7 的發(fā)行版扇住,下次選擇python3.5的,有windows,mac與linux版本盗胀。
0x0112 選擇pycharm
工欲善其事艘蹋,必先利其器
Python的IDE有很多,當(dāng)然你也可以選擇不用票灰,但是我選的是Pycharm
0x0113
需要安裝的一些插件
pip install flask_bootstrap
pip install flask_moment
pip install docx
pip install pymysql
缺了的套件都可以用 pip install來(lái)安裝
0x02 架構(gòu)
大體來(lái)說(shuō)女阀,代碼架構(gòu)是這樣的
flack/
├── app.py
├── database.py
├── models.py
├── home.py
├── templates/
| └── some template html files
├── static/
| └── client-side js and css files
└── requirements.txt
0x021 大致框架
flask的框架大概是這樣的:
- app.py的主要內(nèi)容是包括頁(yè)面顯示,后臺(tái)處理屑迂,Ajax服務(wù)以及自定義的頁(yè)面面模版管道
- database.py與models.py主要是做數(shù)據(jù)持久化浸策,以及數(shù)據(jù)實(shí)體的處理
- home.py是處理flask部署的時(shí)候設(shè)置
0x022 簡(jiǎn)單的后臺(tái)處理流程
而頁(yè)面處理主要是:
"""
初始化flask框架
"""
app = Flask(__name__)
"""
初始化bootstrap前端樣式
"""
bootstrap = Bootstrap(app)
@app.route('/index')
def index():
SQLQuery = '''select `user`.type_id,count(*)
from attend,`user`
where attend.user_id=`user`.id
and attend.`status`=TRUE
and attend.meeting_id = %s
GROUP BY `attend`.cardtype_id'''
result = db_session.execute(SQLQuery % getMeeting_id())
return render_template('index.html', result=result)
- @app.route('/index') 表示uri,資源的訪問(wèn)路徑
- def index(): 定義一個(gè)函數(shù)惹盼,可以與路徑名不一樣庸汗,但是必須唯一,不支持函數(shù)重載
- return render_template('index.html', result=result) 啪啦啪啦處理完了之后手报,通過(guò)render_template來(lái)與模版一起渲染蚯舱,通過(guò)ninja引擎,來(lái)實(shí)現(xiàn)展現(xiàn)頁(yè)面
0x023 簡(jiǎn)單的頁(yè)面前端實(shí)現(xiàn)
必須實(shí)現(xiàn)框架的基本模版掩蛤,擴(kuò)展的是bootstrap框架的模版
base.html
{% extends "bootstrap/base.html" %}
{% block title %}資料管理系統(tǒng){% endblock %}
{% block navbar %}
<div class="navbar navbar-inverse" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/index">參會(huì)人員管理系統(tǒng)</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a href="/zhengjian/jizhe">記者證</a></li>
<li><a href="/zhengjian/lan">藍(lán)色工作證</a></li>
<li><a href="/zhengjian/lv">綠色工作證</a></li>
<li><a href="/zhengjian/anbao">安保證</a></li>
</ul>
</div>
</div>
</div>
{% endblock %}
{% block content %}
<div class="container">
{% block page_content %}{% endblock %}
</div>
{% endblock %}
{% block scripts %}
{{ super() }}
{{ moment.include_moment() }}
{{ moment.lang('zh-CN') }}
<script type="application/javascript">
$SCRIPT_ROOT = {{ request.script_root|tojson|safe }}
</script>
{% endblock %}
index.html
{% extends "base.html" %}
{% block title %}資料管理系統(tǒng){% endblock %}
{% block page_content %}
<h1>本次會(huì)議共制卡<b id="sum"></b>張枉昏,具體情況如下:</h1>
<div>
{% if result %}
{% for r in result %}
<li id="result">{{ r[0]|getTypeRealName }}:{{ r[1] }}</li>
{% endfor %}
{% endif %}
</div>
<div>
如果顯示效果不好,請(qǐng)點(diǎn)擊下載<a href="/static/Soft/Chrome.exe">谷歌瀏覽器</a>
</div>
{% endblock %}
{% block scripts %}
{{ super() }}
<script type="text/javascript">
$(function () {
$sum=0;
$('li#result').each(function () {
li = $(this);
text = li.text();
array = text.split(':');
$sum+=parseInt(array[1]);
});
$('#sum').text($sum);
});
</script>
{% endblock %}
index.html是通過(guò)獲得后臺(tái)的數(shù)據(jù)通過(guò)ninja引擎渲染獲得的顯示頁(yè)面
0x024 數(shù)據(jù)對(duì)象持久化處理
最早開發(fā)的時(shí)候是用sqlite盏档,后來(lái)改用mysql
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
#'sqlite:///./card.db'
engine = create_engine('mysql+pymysql://root:root@localhost/card?charset=utf8', convert_unicode=False)
# 創(chuàng)建數(shù)據(jù)庫(kù)引擎( 當(dāng)前目錄下保存數(shù)據(jù)庫(kù)文件)
db_session = scoped_session(sessionmaker(autocommit=False,
autoflush=False,
bind=engine))
Base = declarative_base()
Base.query = db_session.query_property()
def init_db():
# 在這里導(dǎo)入所有的可能與定義模型有關(guān)的模塊凶掰,這樣他們才會(huì)合適地
# 在 metadata 中注冊(cè)。否則,您將不得不在第一次執(zhí)行 init_db() 時(shí)
# 先導(dǎo)入他們懦窘。
import models
Base.metadata.create_all(bind=engine)
0x025 數(shù)據(jù)實(shí)體定義
一個(gè)數(shù)據(jù)實(shí)體類的定義前翎,脫離了實(shí)際的數(shù)據(jù)庫(kù),很簡(jiǎn)單的完成CRUD的操作
class User(Base):
__tablename__ = 'User'
id = Column(Integer, primary_key=True)
name = Column(String(50), unique=True)
type_id = Column(Integer, ForeignKey('CardType.id'))
department = Column(String(250))
status = Column(BOOLEAN, default=True)
path = Column(String(500))
updatetime = Column(String(500), default=datetime.datetime.now())
batchlog = relationship('BatchLog', backref='users', lazy='dynamic')
@hybrid_property
def getUpdateTime(self):
time = datetime.datetime.strptime(self.updatetime, '%Y-%m-%d %H:%M:%S.%f')
return time - datetime.timedelta(hours=8)
0x03 開發(fā)
光靠上面的只言片語(yǔ)畅涂,想完成一個(gè)flask的demo港华,或許還是有點(diǎn)困難的,有問(wèn)題可以問(wèn)我午衰,或者看看flask的入門教程立宜,也可以看看鄙人的代碼。記錄一下掉過(guò)的坑臊岸。
0x031 中文編碼
python2.7處理中文永遠(yuǎn)繞不過(guò)的就是中文編碼的問(wèn)題橙数,那個(gè)該死的‘utf8’,下一次我一定用python3.5了
0x0311 mysql編碼
我怎么設(shè)置mysql的字符集
show variables like 'character%';
mysql死活出來(lái)的亂碼帅戒,于是使用了下面這一招
engine = create_engine("mysql+pymysql://root:root@localhost/card?charset=utf8", convert_unicode=False)
# 創(chuàng)建數(shù)據(jù)庫(kù)引擎( 當(dāng)前目錄下保存數(shù)據(jù)庫(kù)文件)
db_session = scoped_session(sessionmaker(autocommit=False,
autoflush=False,
bind=engine))
0x0312 路徑中文編碼
普通變量灯帮,使用decode('utf8')函數(shù)
type.decode('utf8')
路徑拼接,是用強(qiáng)制unicode的字符串
rawpath = os.path.join(rawpath, u'記者證')
0x032 word文檔的仿宋字體設(shè)置
在docx的源碼逻住,-->Lib-->site-packages-->docx-->text-->font.py
@name.setter
def name(self,value):
rPr = self._element.get_or_add_rPr()
rPr.rFonts_ascii = value
rPr.rFonts_hAnsi = value
#加上下面一句钟哥,亞洲字體
rPr.rFonts_eastAsia = value
一般的段落是
acceptorparagraph.runs[0].font.name = u'仿宋_GB2312'
acceptorparagraph._element.r_lst[0].rPr.rFonts.set(qn('w:eastAsia'), u'仿宋_GB2312')
表格的話是
row_cells[0].paragraphs[0].runs[0].font.name = u'仿宋_GB2312'
row_cells[0].paragraphs[0].runs[0].font.element.rPr.rFonts.eastAsia = u'仿宋_GB2312'
0x04 心得
其實(shí)整個(gè)程序開發(fā)還是費(fèi)了不少勁,但是成效還是很明顯了:
- 給我數(shù)據(jù)的人可以通過(guò)訪問(wèn)網(wǎng)頁(yè)來(lái)查看相關(guān)參會(huì)人員的是否已經(jīng)被成功接受
- 所有人都能通過(guò)網(wǎng)頁(yè)得知當(dāng)前的證卡制作情況
- 我理直氣壯的提交做好的證件瞎访,不會(huì)被人說(shuō)做漏了腻贰,也不會(huì)發(fā)生移交了卡發(fā)生丟失還賴我
- 我能在任何時(shí)間統(tǒng)計(jì)出已經(jīng)做了多少卡的數(shù)量,畢竟是工作量啊扒秸,不然總是個(gè)模糊數(shù)播演,豈不是“葫蘆僧盤葫蘆案”
- 移交證卡的數(shù)量和名單列表自動(dòng)生成,就不用每次都重新數(shù)鸦采,還容易出錯(cuò)宾巍,不然還說(shuō)不清楚交出去多少
- 連報(bào)賬材料都自動(dòng)生成了,以后再也不用費(fèi)時(shí)做這些體力活了渔伯,只要排版打印就好
- 只要我能說(shuō)清楚了我做了啥顶霞,那就意味著標(biāo)準(zhǔn)在我手里,獲得了標(biāo)準(zhǔn)的制定權(quán)就是拿到了游戲規(guī)則