來源:Python 技術(shù)「ID: pythonall」
上一期,談了如何用 Python 打造運營系統(tǒng) 的過程,雖然以及很方便了,但是還有很多需要人工執(zhí)行的地方,不是特別方便。
更重要的是無法及時為大家提供實時數(shù)據(jù),加上有時工作繁忙可以忘掉,實屬不便。
那么再進一步 —— 做成在線版的,可以隨時瀏覽,方便快捷,還等什么,開干吧。
規(guī)劃上一期,做的工作主要是數(shù)據(jù)整合,數(shù)據(jù)處理,報表數(shù)據(jù)輸出,已經(jīng)做了大部分工作,現(xiàn)在的問題是如何 Web 化。
Web 化的主要目的是,讓用戶通過瀏覽器訪問到報表數(shù)據(jù),所以改造的重點在于將數(shù)據(jù)展示部分的用前端技術(shù)實現(xiàn)。
于是需要做的是,設(shè)計展示頁面 ,定義與后端的 數(shù)據(jù)接口 ,選擇服務(wù)器 部署 。
而其中重點的工作是 展示頁面 和 數(shù)據(jù)接口。
在進行具體工作之前,還有個一個重要工作就是 —— 選擇技術(shù)路線 ,即用什么技術(shù)做 Web 化實現(xiàn)。
那需要基于 Python 的 Web 框架來說,可以選擇 Flask 和 Django。
鑒于項目比較小,另外之前寫了一系列 Flask 的文章 [1] ,對 Flask 比較熟,所以選擇了 Flask。
然后將之前實現(xiàn)的功能作為 Web 項目的外部模塊引入,承擔(dān)數(shù)據(jù)讀取任務(wù)。
因為 Web 服務(wù)主要是通過數(shù)據(jù)讀取的,對于打卡數(shù)據(jù)的抓取,和成員積分的計算,需要定時任務(wù)來完成,雖然可以在 Web 服務(wù)中利用 scheduled 來寫計劃任務(wù),為了簡便,直接使用 Linux 服務(wù)的 crontab [2] 命令來定時執(zhí)行。
展示頁面之前寫過一篇 nginx 日志可視化 ,其中用的是 Bootstrap 的前端框架,直接拿來用。
真是,平時多積累,用時顯身手!
需要的頁面有 打卡頁面、成員數(shù)據(jù)、組長數(shù)據(jù)、開單記錄。
從哪里開始呢?不是具體的頁面,而是從制作模板頁面開始。
模板頁面實現(xiàn)前臺功能,比較省力的方式是使用模板,即,將頁面共同的元素寫在模板上,以重復(fù)利用。
創(chuàng)建一個頁面模板 layout.html
,模板中寫完頁面主題框架,以及引入 css,js 文件,并且為可替換部分預(yù)留區(qū)域。
可替換部分有,樣式,導(dǎo)航菜單、主體頁面、腳本。
因為最后是用 Flask 作為 Web 服務(wù)的,所以采用 Jinja2 [3] 做為模板引擎。
代碼大致如下:
<!DOCTYPE html > <html lang ="zh-CN" > <head > <meta charset ="utf-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1" > <title > 訓(xùn)練營311班</title > <link href ="../static/css/bootstrap.min.css" type ='text/css' rel ="stylesheet" > <link href ="../static/css/dashboard.css" rel ="stylesheet" > {% block head %} {% endblock %}</head > <body > <!--導(dǎo)航欄--> <nav class ="navbar navbar-inverse navbar-fixed-top" > <div class ="container-fluid" > <div class ="navbar-header" > <button type ="button" class ="navbar-toggle collapsed" data-toggle ="collapse" data-target ="#navbar" aria-expanded ="false" aria-controls ="navbar" > <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 ="/" > 銷售訓(xùn)練營 311班</a > </div > <div id ="navbar" class ="navbar-collapse collapse" > <ul class ="nav navbar-nav" > {% block menu%} {% endblock %} </ul > </div > </div > </nav > <div class ="container-fluid" > <div class ="row" > {% block main%} {% endblock %} </div > </div > <script src ="../static/js/bootstrap.min.js" type ="text/javascript" > </script > {% block script %} {% endblock %}</body > </html >
可以看出頁面采用的是 Bootstrap 框架 [4] ,加載了 Bootstrap 必要的 Css 和 js 庫。
然后加入了頁面框架,并預(yù)留了菜單、頁面主體、定制的腳本位置。
打卡頁面打卡頁面需要展示打卡率,最好的方式是用圖表。
首選 ECharts [5] ,因為 ECharts 很成熟,而且易用。
然后在 ECharts 示例 [6] 中找到合適的圖表。
值得點贊的是,ECharts 示例中,提供了示例代碼,直接復(fù)制使用即可。
我們選用 柱狀圖標(biāo)簽旋轉(zhuǎn) [7]
柱狀圖標(biāo)簽旋轉(zhuǎn) 將代碼復(fù)制到頁面中。
圖表數(shù)據(jù)通過 Flask 視圖加載時提供。
部分代碼如下:
{% extends "layout.html" %} {% block menu %}<li class ="active" > <a href ="/check_rate" > 打卡率</a > </li > <li > <a href ="/memberdata" > 成員數(shù)據(jù)</a > </li > <li > <a href ="/teamleader" > 組長數(shù)據(jù)</a > </li > <li > <a href ="/sale" > 開單記錄</a > </li > {% endblock %} {% block main %}<div class ="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main" > <h1 class ="page-header" > {{title}}</h1 > <div class ="row placeholders" > <div class ="col-xs-12 col-sm-8 col-lg-10 placeholder" style ="height:500px;" id ="main" > </div > <div class ="col-lg-4 col-lg-offset-4 col-sm-6 col-sm-offset-3 col-xs-8 col-xs-offset-2" > <div id ="detail" > 點擊圖表查看 打卡詳情</div > </div > </div > </div > {% endblock %} {% block script %}<script src ="../static/js/jquery.2.0.3.min.js" type ="text/javascript" > </script > <script src ="../static/js/echarts5.min.js" charset ="utf-8" > </script > <script type ="text/javascript" > var app = {}; var chartDom = document .getElementById('main' ); var myChart = echarts.init(chartDom); ...<省略>... var option = { title : { show : true , left : 'center' , top : 'top' }, ...<省略>... dataset: { source : {{ data | safe }} }, ...<省略>... } ...<省略>... {% endblock %}
{% extends "layout.html" %}
引入模板頁面 layout.html
{% block menu %}
、{% block main %}
等 為替換的內(nèi)容source: {{ data | safe }}
的意思是 使用視圖中的值 data
填充到這里,后面的 safe
是一個過濾器,表示替換部分不做 Html 安全字符轉(zhuǎn)化 數(shù)據(jù)縮放這里還需要解決一個問題,就是隨著日期的增加,圖表就是很密,不好觀看,ECharts 提供一種圖表縮放工具 DataZoom
,
可以通過拖動和改變大小控制數(shù)據(jù)的展示范圍,配置很簡單,加在圖表的配置中就好了,例如我的寫法是:
dataZoom: [{ type : 'slider' , start : 60 , end : 100 }],
start
和 end
表示數(shù)據(jù)范圍開始和結(jié)束的百分比,這一點特別贊 詳細(xì)數(shù)據(jù)圖表展示后,如果想知道某一天某個組詳細(xì)的打卡數(shù)據(jù)怎么辦?
為了便于使用,利用 EChart 的事件機制,當(dāng)點擊一個柱狀圖時,顯示出詳細(xì)信息。
實現(xiàn)很簡單,就是個圖表對象加載一個事件,在事件里,使用 Ajax [8] 加載并顯示數(shù)據(jù)就好:
myChart.on('click' , 'series' , function (params ) { console .log(params); $.ajax({ url : "/api/teamcheckdetail" , data : { team : params.seriesName, date : params.name } }).done(function (response ) { data = response.data; $("#detail" ).jsGrid({ editing : false , sorting : false , autoload : false , data : data, fields : [{ 'name' : 'name' , 'title' : '昵稱' }, { 'name' : 'check' , 'title' : '打卡' }] }); }); });
/api/teamcheckdetail
為后臺數(shù)據(jù)接口(詳見 API 設(shè)計)$("#detail").jsGrid()
是一個 jsGrid 做的表格,加載方法是重新炫耀一個表格到此,最復(fù)雜的打卡頁面就搞定了,看看效果吧:
打卡率 數(shù)據(jù)列表成員數(shù)據(jù)、組長數(shù)據(jù) 以及 開單數(shù)據(jù),都是用數(shù)據(jù)表格展示的,使用了 jsGrid [9] 框架,主要是方便易用。
在頁面上定義一個 div
,并設(shè)置 id,然后用 jsGrid 方法渲染一些就可以了,和簡單。
其實在上一節(jié),我們以及看到如何渲染了,需要做的事定義后臺 API 接口,通過 Ajax 加載數(shù)據(jù)就可以了:
$("#jsGrid" ).jsGrid({ height : '600' , width : "100%" , editing : false , sorting : false , autoload : true , controller : { loadData : function ( ) { var d = $.Deferred(); $.ajax({ url : "/api/memberdata" , }).done(function (response ) { d.resolve(response.data); }); return d.promise(); } }, fields : [{'name' : '組' , type : "text" }, {'name' :'昵稱' , type : "text" }, {'name' :'開單' , type : "text" }, {'name' :'分享' , type : "text" }, {'name' :'打卡' , type : "text" }, {'name' :'提問' , type : "text" }, {'name' :'解答整理' , type : "text" }, {'name' :'積分' , type : "text" }, ] });
controller
里的 loadData
在頁面元素繪制完成調(diào)用loadData
方法返回一個 promise [10] ,等數(shù)據(jù)獲取成功之后,會渲染到頁面上那么 組長數(shù)據(jù) 和 開單數(shù)據(jù) 是類似的,就不贅述了。
數(shù)據(jù)接口Flask 實現(xiàn)后臺接口,特別簡單,只需要再接口相應(yīng)方法前,加句注解 [11] 就好了,例如 打卡率的接口:
@app.route('/') @app.route('/check_rate') def check_rate () : data = dataSource.show_check_rate(rtype='dict' ) ret = [['date' , '1組' , '2組' , '3組' , '4組' , '5組' ]] for d in data: row = [] for k in d: row.append(d[k]) ret.append(row) return render_template('check_rate.html' , title='打卡率' , data=ret)
@app.route
是個注解,以指定 URL 路徑,由 check_rate
方法做相應(yīng)dataSource
是對上一期數(shù)據(jù)處理功能的一個封裝,調(diào)用相應(yīng)方法,獲取數(shù)據(jù)render_template
是 Flask 響應(yīng)頁面合成的方法,第一個參數(shù)是模板頁面,后面的命名參數(shù)是需要替換的數(shù)據(jù)打卡率頁面的數(shù)據(jù),是合成響應(yīng)頁面時提供的,還有一些頁面需要前臺通過 Ajax 方式獲取,接口怎么寫呢?
用獲取成員數(shù)據(jù)作為例子:
@app.route('/api/memberdata') def api_memberdata () : data = {'data' : dataSource.show_member_score(rtype='dict' )} return jsonify(data)
通過封裝的數(shù)據(jù)處理類,得到數(shù)據(jù) 調(diào)用 Flask 的 jsonify
方法將數(shù)據(jù)作為 json 響應(yīng)提供給前臺 那么,其他的頁面響應(yīng)和數(shù)據(jù)響應(yīng)接口是類似的,更詳細(xì)的代碼,見代碼示例。
部署主要的開發(fā)工作完成后,就可以部署了。
之前寫過 部署 Flask [12] ,用的是 uWSGI ,剛好照搬。
如果沒有 git 或者 svn 的代碼管理,就直接將復(fù)制到服務(wù)器上,安裝 Flask,就可以啟動了。
python app.py
沒有問題,配置一下 Nginx [13] 的反向代理:
server { listen 80; server_name stc.example.com; access_log /var/log/nginx/access_stc.log main; location / { proxy_buffers 8 1024k; proxy_buffer_size 1024k; proxy_pass http://127.0.0.1:5000; } }
server_name
為域名,也就是通過這個地址可以訪問到系統(tǒng)的,在此之前需要申請并備案域名 [14] location
里的 proxy_pass 是服務(wù)器上我們的 Web 服務(wù)的地址Nginx 的作用是做一個代理,將通過 stc.example.com 的訪問轉(zhuǎn)發(fā)到 真實地址上 配置完成之后,重啟一下 Nginx 服務(wù),如果沒有問題,就可以通過設(shè)置的域名訪問了。
維護開發(fā)部署完成,還不是真的完成,還需要做運維。
以這個項目為例,運維工作主要有兩部分:
只有不斷地做數(shù)據(jù)處理,才能及時更新數(shù)據(jù),這個工作對于 Web 系統(tǒng)來說,屬于支撐性的,所以沒必要讓 Web 系統(tǒng)來負(fù)責(zé)。
在 Linux 系統(tǒng)上,有個 crontab [15] 命令,用它做定時數(shù)據(jù)數(shù)據(jù),好處是開發(fā)量少,另外不會因為 Web 系統(tǒng)的問題導(dǎo)致數(shù)據(jù)處理不及時。
服務(wù)器維護,就是保證服務(wù)器不會因為異常停止工作,一般使用像 uWSGI 類的服務(wù)器都有自維護功能,如果沒有使用的話,就需要自己處理了,比如寫個監(jiān)控腳本,當(dāng)發(fā)現(xiàn)服務(wù)器不工作時,重啟服務(wù),并發(fā)送通知。
強烈建議 使用安全的 Web 服務(wù)器,特別是在生產(chǎn)環(huán)境中
總結(jié)到此,系統(tǒng)的搭建完成了,再也不用我每天為生成報表耗費時間了,可以將更多的時間用在更重要的事情上了。
如果你認(rèn)真讀了,就會發(fā)現(xiàn),每一塊的知識技術(shù),都是很簡單的,但如何將這些簡單的點拼接起來,是需要大量的知識記錄和歷練的。
而我之所以可以這樣做,是因為在這里的長期輸出,從最基礎(chǔ)的 Web 系統(tǒng)開始,一點一點了解整個 Web 系統(tǒng)需要的基礎(chǔ),當(dāng)需要完成一個系統(tǒng)時,大部分都是來自平時的積累。
期望這篇文章對你有所啟發(fā),一切都源自持續(xù)積累 ,比心!
參考資料 [1] Flask: https://mp.weixin.qq.com/s/jBom8hpmypTvdZLeZD-s_A
[2] crontab: https://www.runoob.com/linux/linux-comm-crontab.html
[3] Jinja2 模板引擎: https://jinja./en/3.0.x/
[4] Bootstrap 框架: https:///
[5] ECharts: https://echarts./zh/index.html
[6] ECharts 示例: https://echarts./examples/zh/index.html
[7] 柱狀圖標(biāo)簽旋轉(zhuǎn): https://echarts./examples/zh/editor.html?c=bar-label-rotation
[8] Ajax: https://developer.mozilla.org/zh-CN/docs/Web/Guide/AJAX
[9] jsGrid: http:///getting-started/
[10] promise 對象: https://es6./#docs/promise
[11] Python 注解: https://www.jianshu.com/p/7a644520418b
[12] 部署 Flask: https://mp.weixin.qq.com/s/b9Mmp0bSCmNVDzaExJlJ0w
[13] Nginx: https://www./
[14] 域名備案: https://baike.baidu.com/item/%E5%9F%9F%E5%90%8D%E5%A4%87%E6%A1%88/1131453
[15] crontab: https://www.runoob.com/linux/linux-comm-crontab.html