CMS Projects

內容管理的近端與雲端

天涯若比鄰的協同工作概念, 讓 GIT 版次管理得以跳脫持續網路連線的束縛, 可以說是內容管理的一大進步.

而過去近端程式的編寫模式, 一旦套用到 OpenShift 雲端平台, 在資料邏輯與頁面展示分離的架構下, 彼此透過關鍵數據的清晰連結, 也能夠從你儂我儂的混亂局面中, 得到適度的切割與解放.

現在即使協同專案以近端即遠端的模式運行, 在專業技術界面有效分隔下, 也能讓不同領域的內容管理人員, 各自獨領風騷, 恣意發揮最大的創意與效能.

這裡將利用一個簡單的猜數字程式, 從單機近端模式出發, 經過邏輯與展示混用, 一直到邏輯與展示分割的案例:

#coding: utf-8
# 猜數字遊戲
import random
 
標準答案 = random.randint(1, 100)
你猜的數字 = int(input("請輸入您所猜的整數:"))
猜測次數 = 0
while 標準答案 != 你猜的數字:
    if 標準答案 < 你猜的數字:
        print("太大了,再猜一次 :)加油")
    else:
        print("太小了,再猜一次 :)加油")
    你猜的數字 = int(input("請輸入您所猜的整數:"))
    猜測次數 += 1
 
print("猜對了!總共猜了", 猜測次數, "次")

 運算邏輯與展示混用:

#coding: utf-8
import cherrypy
import random
class Guess(object):
    # 標準答案必須利用 session 機制儲存
    _cp_config = {
    # 配合 utf-8 格式之表單內容
    # 若沒有 utf-8 encoding 設定,則表單不可輸入中文
    'tools.encode.encoding': 'utf-8',
    # 加入 session 設定
    'tools.sessions.on' : True,
    'tools.sessions.storage_type' : 'file',
    'tools.sessions.locking' : 'explicit',
    # 就 OpenShift ./tmp 位於 app-root/runtime/repo/tmp
    'tools.sessions.storage_path' : './tmp',
    # 內定的 session timeout 時間為 60 分鐘
    'tools.sessions.timeout' : 60
    }
    @cherrypy.expose
    def index(self, guess=None):
        # 將標準答案存入 answer session 對應區
        cherrypy.session['answer'] = random.randint(1, 100)
        cherrypy.session['count'] = 0
        超文件輸出 = "<form method=POST action=doCheck>"
        超文件輸出 += "請輸入您所猜的整數:<input type=text name=guess><br />"
        超文件輸出 += "<input type=submit value=send>"
        超文件輸出 += "</form>"
        return 超文件輸出
    @cherrypy.expose
    def default(self, attr='default'):
        # 內建 default 方法, 找不到執行方法時, 會執行此方法
        return "Page not Found!"
    @cherrypy.expose
    def doCheck(self, guess=None):
        # 假如使用者直接執行 doCheck, 則設法轉回根方法
        if guess is None:
            raise cherrypy.HTTPRedirect("/")
        # 從 session 取出 answer 對應資料
        標準答案 = int(cherrypy.session.get('answer'))
        # 經由表單所取得的 guess 資料型別為 string
        try:
            你猜的數字 = int(guess)
        except:
            return "請輸入整數!<br />"+"<br /><a href=/>再試一次!<br />"
        # html 表單
        表單 = '''<form method="POST" action="doCheck">
                請輸入您所猜的整數:<input type="text" name="guess"><br />
                <input type="submit" value="send">
                </form>
                '''
        cherrypy.session['count']  += 1
        if 標準答案 < 你猜的數字:
            超文件輸出 = "太大了,再猜一次 :)加油<br />"+str(標準答案)+"<br />"
            超文件輸出 += 表單
        elif 標準答案 > 你猜的數字:
            超文件輸出 = "太小了,再猜一次 :)加油<br />"+str(標準答案)+"<br />"
            超文件輸出 += 表單
        else:
            猜測次數 = cherrypy.session.get('count')
            超文件輸出 = "猜對了!答案是:"+str(標準答案)+", 總共猜了"+str(猜測次數)+"次<br />"
            超文件輸出 += "<br /><a href=/>再玩一次!<br />"
        return 超文件輸出
        
application = cherrypy.Application(Guess())

 運算邏輯與展示流程, 利用 Mako template engine 切割:

#coding: utf-8
import cherrypy
import random
# for path setup
import os
# for mako
from mako.lookup import TemplateLookup
template_root_dir = os.environ['OPENSHIFT_REPO_DIR']+"/wsgi/static"
SQLite_data_dir = os.environ['OPENSHIFT_DATA_DIR']+"/db"
download_root_dir = os.environ['OPENSHIFT_DATA_DIR']
class Guess(object):
    # 標準答案必須利用 session 機制儲存
    _cp_config = {
    # 配合 utf-8 格式之表單內容
    # 若沒有 utf-8 encoding 設定,則表單不可輸入中文
    'tools.encode.encoding': 'utf-8',
    # 加入 session 設定
    'tools.sessions.on' : True,
    'tools.sessions.storage_type' : 'file',
    'tools.sessions.locking' : 'explicit',
    # 就 OpenShift ./tmp 位於 app-root/runtime/repo/tmp
    'tools.sessions.storage_path' : './tmp',
    # 內定的 session timeout 時間為 60 分鐘
    'tools.sessions.timeout' : 60,
    'tools.mako.directories' :  template_root_dir+"/templates"
    }
    @cherrypy.expose
    def index(self, guess=None):
        # 將標準答案存入 answer session 對應區
        theanswer = random.randint(1, 100)
        thecount = 0
        # 將答案與計算次數變數存進 session 對應變數
        cherrypy.session['answer'] = theanswer
        cherrypy.session['count'] = thecount
        套稿查詢 = TemplateLookup(directories=[template_root_dir+"/templates"])
        # 必須要從 templates 目錄取出 index.html
        內建頁面 = 套稿查詢.get_template("index.html")
        return 內建頁面.render()
    @cherrypy.expose
    def default(self, attr='default'):
        # 內建 default 方法, 找不到執行方法時, 會執行此方法
        套稿查詢 = TemplateLookup(directories=[template_root_dir+"/templates"])
        # 必須要從 templates 目錄取出 index.html
        內建頁面 = 套稿查詢.get_template("default.html")
        return 內建頁面.render()
    @cherrypy.expose
    def doCheck(self, guess=None):
        # 假如使用者直接執行 doCheck, 則設法轉回根方法
        if guess is None:
            raise cherrypy.HTTPRedirect("/")
        # 從 session 取出 answer 對應資料, 且處理直接執行 doCheck 時無法取 session 值情況
        try:
            theanswer = int(cherrypy.session.get('answer'))
        except:
            raise cherrypy.HTTPRedirect("/")
        套稿查詢 = TemplateLookup(directories=[template_root_dir+"/templates"])
        # 必須要從 templates 目錄取出 index.html
        內建頁面 = 套稿查詢.get_template("docheck.html")
        # 經由表單所取得的 guess 資料型別為 string
        try:
            theguess = int(guess)
        except:
            return 內建頁面.render(輸入="error")
        cherrypy.session['count']  += 1
        if theanswer < theguess:
            return 內建頁面.render(輸入="big", theanswer=theanswer)
        elif theanswer > theguess:
            return 內建頁面.render(輸入="small", theanswer=theanswer)
        else:
            thecount = cherrypy.session.get('count')
            return 內建頁面.render(輸入="exact", theanswer=theanswer, thecount=thecount)
application_conf = {'/kmol':{
        'tools.staticdir.on': True,
        'tools.staticdir.dir': os.environ['OPENSHIFT_DATA_DIR']+"/kmol"},
        '/downloads':{
        'tools.staticdir.on': True,
        'tools.staticdir.root': download_root_dir,
        'tools.staticdir.dir': 'downloads',
        'tools.staticdir.index' : 'index.htm'
        },
        # 設定靜態 templates 檔案目錄對應
        '/templates':{
        'tools.staticdir.on': True,
        'tools.staticdir.root': template_root_dir,
        'tools.staticdir.dir': 'templates',
        'tools.staticdir.index' : 'index.htm'
        }
    }
application = cherrypy.Application(Guess(), config = application_conf)

 其中 index.html:

##encoding: utf-8
## 兩個 pound signs 在 Mako 語法中為註解行
## 多行註解則使用
<%doc>
    這是註解
    使用 <%! %> 表示在模組層次運作的 Python 程式碼, 可以用來 import 模組或定義相關的函式或物件
</%doc>
<html>
        <head>
                <title>猜數字遊戲 index.html</title>
<%include file="meta.html"/>
        </head>
        <body>
<style type="text/css" media="all">
@import "templates/style/base.css";
</style>  
## 以下為一般的 Python 程式碼, 若是一般的運作則需要將 rows 數列傳進套稿中, 以便 render 頁面
<%
%>
猜數字遊戲(來自 Mako) <br /><br />
請選擇介於 1 到 100 的整數 <br /><br />
## ${outString}
## CherryPy 送出 st_array 數列資料
## 應該要先將 table 資料由左至右的排列序號, 傳道 Mako, 然後利用以下迴圈將資料列印成表格
    <form method=POST action=doCheck>
    請輸入您所猜的整數:<input type=text name=guess><br />
    <input type=submit value=send>
    </form>
##<%include file="footer.html"/>
        </body>
</html>

 default.html:

##encoding: utf-8
## 兩個 pound signs 在 Mako 語法中為註解行
## 多行註解則使用
<%doc>
    這是註解
    使用 <%! %> 表示在模組層次運作的 Python 程式碼, 可以用來 import 模組或定義相關的函式或物件
</%doc>
<html>
        <head>
                <title>default.html</title>
<%include file="meta.html"/>
        </head>
        <body>
<style type="text/css" media="all">
@import "templates/style/base.css";
</style>  
## 以下為一般的 Python 程式碼, 若是一般的運作則需要將 rows 數列傳進套稿中, 以便 render 頁面
<%
%>
找不到頁面!(來自 Mako) <br /><br />
<a href="/">再試一次!</a><br />
## ${outString}
##<%include file="footer.html"/>
        </body>
</html>

 docheck.html:

##encoding: utf-8
## 兩個 pound signs 在 Mako 語法中為註解行
## 多行註解則使用
<%doc>
    這是註解
    使用 <%! %> 表示在模組層次運作的 Python 程式碼, 可以用來 import 模組或定義相關的函式或物件
</%doc>
<html>
        <head>
                <title>docheck.html</title>
<%include file="meta.html"/>
        </head>
        <body>
<style type="text/css" media="all">
@import "templates/style/base.css";
</style>  
## 以下為一般的 Python 程式碼, 若是一般的運作則需要將 rows 數列傳進套稿中, 以便 render 頁面
<%
%>
% if 輸入 is "error":
    請輸入整數!<br /><br /><a href="/">再試一次!</a><br />
% elif 輸入 is "big":
    太大了,再猜一次 :)加油<br />${theanswer}<br />
    <form method=POST action=doCheck>
    請輸入您所猜的整數:<input type=text name=guess><br />
    <input type=submit value=send>
    </form>
% elif 輸入 is "small":
    太小了,再猜一次 :)加油<br />${theanswer}<br />
    <form method=POST action=doCheck>
    請輸入您所猜的整數:<input type=text name=guess><br />
    <input type=submit value=send>
    </form>
% elif 輸入 is "exact":
    猜對了!答案是:${theanswer}, 總共猜了${thecount}次<br />
    <br /><a href="/">再玩一次!<br />
% else:
    請輸入整數!<br /><br /><a href="/">再試一次!</a><br />
% endif
## ${outString}
##<%include file="footer.html"/>
        </body>
</html>

 meta.html:

<meta http-equiv="content-type" content="text/html;charset=utf-8">

 最後, 則是利用一組網際程式碼直接在近端與遠端執行, 在近端 wsgi 目錄下必須建立 data, db, downloads 與 tmp 目錄, 其中 data/kmol 代表近端程式的 data 目錄, db 為資料庫存檔區, downloads 則是近端上傳檔案的擺放目錄.

而程式碼則透過 os.environ.keys() 是否存在特定 OpenShift key 來判定程式在遠端或近端執行.

#coding: utf-8
import cherrypy
import random
# for path setup
import os
# for mako
from mako.lookup import TemplateLookup
cwd = os.getcwd()
if 'OPENSHIFT_REPO_DIR' in os.environ.keys():
    # 表示程式在雲端執行
    template_root_dir = os.environ['OPENSHIFT_REPO_DIR']+"/wsgi/static"
    SQLite_data_dir = os.environ['OPENSHIFT_DATA_DIR']+"/db"
    download_root_dir = os.environ['OPENSHIFT_DATA_DIR']
    data_file = os.environ['OPENSHIFT_DATA_DIR']
else:
    # 表示程式在近端執行
    template_root_dir = cwd+"/static"
    SQLite_data_dir = cwd+"/db"
    download_root_dir = cwd+"/downloads"
    data_file = cwd+"/data"
class Guess(object):
    # 標準答案必須利用 session 機制儲存
    _cp_config = {
    # 配合 utf-8 格式之表單內容
    # 若沒有 utf-8 encoding 設定,則表單不可輸入中文
    'tools.encode.encoding': 'utf-8',
    # 加入 session 設定
    'tools.sessions.on' : True,
    'tools.sessions.storage_type' : 'file',
    'tools.sessions.locking' : 'explicit',
    # 就 OpenShift ./tmp 位於 app-root/runtime/repo/tmp
    'tools.sessions.storage_path' : './tmp',
    # 內定的 session timeout 時間為 60 分鐘
    'tools.sessions.timeout' : 60,
    'tools.mako.directories' :  template_root_dir+"/templates"
    }
    @cherrypy.expose
    def index(self, guess=None):
        # 將標準答案存入 answer session 對應區
        theanswer = random.randint(1, 100)
        thecount = 0
        # 將答案與計算次數變數存進 session 對應變數
        cherrypy.session['answer'] = theanswer
        cherrypy.session['count'] = thecount
        套稿查詢 = TemplateLookup(directories=[template_root_dir+"/templates"])
        # 必須要從 templates 目錄取出 index.html
        內建頁面 = 套稿查詢.get_template("index.html")
        return 內建頁面.render()
    @cherrypy.expose
    def default(self, attr='default'):
        # 內建 default 方法, 找不到執行方法時, 會執行此方法
        套稿查詢 = TemplateLookup(directories=[template_root_dir+"/templates"])
        # 必須要從 templates 目錄取出 index.html
        內建頁面 = 套稿查詢.get_template("default.html")
        return 內建頁面.render()
    @cherrypy.expose
    def doCheck(self, guess=None):
        # 假如使用者直接執行 doCheck, 則設法轉回根方法
        if guess is None:
            raise cherrypy.HTTPRedirect("/")
        # 從 session 取出 answer 對應資料, 且處理直接執行 doCheck 時無法取 session 值情況
        try:
            theanswer = int(cherrypy.session.get('answer'))
        except:
            raise cherrypy.HTTPRedirect("/")
        套稿查詢 = TemplateLookup(directories=[template_root_dir+"/templates"])
        # 必須要從 templates 目錄取出 index.html
        內建頁面 = 套稿查詢.get_template("docheck.html")
        # 經由表單所取得的 guess 資料型別為 string
        try:
            theguess = int(guess)
        except:
            return 內建頁面.render(輸入="error")
        cherrypy.session['count']  += 1
        if theanswer < theguess:
            return 內建頁面.render(輸入="big", theanswer=theanswer)
        elif theanswer > theguess:
            return 內建頁面.render(輸入="small", theanswer=theanswer)
        else:
            thecount = cherrypy.session.get('count')
            return 內建頁面.render(輸入="exact", theanswer=theanswer, thecount=thecount)
application_conf = {'/kmol':{
        'tools.staticdir.on': True,
        'tools.staticdir.dir': data_file+"/kmol"},
        '/downloads':{
        'tools.staticdir.on': True,
        'tools.staticdir.root': download_root_dir,
        'tools.staticdir.dir': 'downloads',
        'tools.staticdir.index' : 'index.htm'
        },
        # 設定靜態 templates 檔案目錄對應
        '/templates':{
        'tools.staticdir.on': True,
        'tools.staticdir.root': template_root_dir,
        'tools.staticdir.dir': 'templates',
        'tools.staticdir.index' : 'index.htm'
        }
    }
if 'OPENSHIFT_REPO_DIR' in os.environ.keys():
    # 表示在 OpenSfhit 執行
    application = cherrypy.Application(Guess(), config = application_conf)
else:
    # 表示在近端執行
    cherrypy.quickstart(Guess(), config = application_conf)

 guess_prog_v3.7z