2023-02-21 10:33

[Python] Flask Log 配置

import os
import logging
import logging.handlers

from flask import Flask, g, request, json

app = Flask(__name__)


#[ Log 配置 ]#############################################################
 # 用來記錄無法處理的錯誤 (PS: 使用 WSGI 會依附 Apache 的設定,可以不用配置)

# https://docs.python.org/zh-cn/3/library/logging.html
formatter = logging.Formatter("%(asctime)s [%(levelname)s]  %(message)s")

# https://docs.python.org/zh-tw/3/library/logging.handlers.html#timedrotatingfilehandler
handler = logging.handlers.TimedRotatingFileHandler("log/web-api",
    when = "D",
    interval = 1,
    backupCount = 7,
    encoding = "UTF-8",
    delay = False,
    utc = True)
handler.setFormatter(formatter)

app.logger.addHandler(handler)
2023-02-21 10:13

[Python] Flask 自訂日期的 Json 轉換

import os
import datetime
import time

from flask import Flask, g, request, json
from flask.json import JSONEncoder


class CustomJsonEncoder(JSONEncoder):
    # 針對日期自訂 Json 轉換

    def default(self, obj):
        if isinstance(obj, datetime.date):
            return obj.isoformat().replace('T', ' ')

        return super().default(obj)


app = Flask(__name__)

app.json_encoder = CustomJsonEncoder
app.config['JSON_AS_ASCII'] = False  # 返回結果可以正確顯示中文
2023-02-21 10:05

[Python] Flask MySQL 連線管理

import os
import mysql.connector as sql

from werkzeug.exceptions import HTTPException, BadRequest
from flask import Flask, g, request, json


app = Flask(__name__)



#[ DB 處裡 ]#############################################################

# https://docsxyz.com/zh-hant/wiki/python/connector-python-connectargs
db_config = {
    'host'      : "localhost",
    'user'      : "XXXX",
    'passwd'    : "XXXX",
    'db'        : 'XXXX',
    'use_pure'  : True,
    'autocommit': True,
    'charset'   : 'utf8',
}


@app.before_request
def before_request():
    # 在 request 前開啟 DB 連線
    # g 是 Flask global 在每個 request 有獨立的 context

    g.cnt = sql.connect(**db_config)
    g.cursor = g.cnt.cursor(dictionary = True)



@app.after_request
def after_request(response):
    # 在 request 後結束 DB 連線

    cursor = g.get('cursor', None)
    if cursor is not None:
        # 當 cursor 還有 row 沒有取出,close 會發生錯誤
        if cursor.with_rows : cursor.fetchall()
        cursor.close()

    cnt = g.get('cnt', None)
    if cnt is not None:
        cnt.close()

    return response
2023-02-21 09:55

[Python] Flask 筆記

相依套件:
-------------------------------------------------------------------------------
python-3.8.10-amd64.exe

pip install Flask
pip install flask_cors
pip install mysql-connector-python
pip install pycryptodomex
pip install py-linq     # https://viralogic.github.io/py-enumerable/


Apache CGI 配置,用虛擬 Script 指向到 app.cgi
-------------------------------------------------------------------------------


ScriptAlias /web-api  D:/iog-project/web-api/app.cgi

<Directory "D:/iog-project/web-api/">
Options ExecCGI 
AllowOverride all
Require local
</Directory>


-------------------------------------------------------------------------------

# 正常執行
flask run

# 除錯執行  (flask 會提供除錯功能,並將 logger 從 warning 提升到 debug 
export FLASK_ENV=development    # for linux / git  bash 
set FLASK_ENV=development       # for windows cmd
flask run

# 列出設定的路徑
flask routes


其他
-------------------------------------------------------------------------------

因為 web-api 的格式是 Json,所以遵照 JS 的命名風格,欄位名稱開頭小寫第二個字大寫

2023-02-21 09:53

[Python] Flask 錯誤處裡


from werkzeug.exceptions import HTTPException, BadRequest
from flask import Flask, g, request, json

app = Flask(__name__)


#[ 錯誤處裡 ]#############################################################
@app.errorhandler(Exception)
def handle_exception(e):
    if isinstance(e, HTTPException): return e  # 讓 HTTPException 交由下一個處理

    app.logger.exception("Internal Server Error.")  # log 錯誤訊息
    if app.debug : return e

    return json.jsonify({'message': 'Internal Server Error.'}), 500


@app.errorhandler(HTTPException)
def handle_exception(e):
    response = e.get_response()
    return json.jsonify({'message': e.description}), e.code
2022-09-08 13:39

架構解釋

以前在專案開發時對分層架構所定下的原則,去避免不必要的地雷,以及每一層的要處裡的職權。

我會考慮分層架構,是希望提高系統的嚴謹度,以及 Method 的共用性,所以 Dao 層級就要避免功能導向,不然就是把單頁的程式拆分到多個層去而已。

 

DomainModel / ViewModel

  • 這三類都屬於 POCO 類型的物件,單純的資料載體,不允許有動作的 method,不可以外部取資料
  • 相同資料的欄位命名必須一致,反例: ProjectId, Pid, pid, PID, p_id
  • 有欄位就要有資料,明明有欄位定義卻不從 DB 取資料,這會造成不必要的雷

 

DomainModel

  • 欄位定義可與 DB 一致
  • 將 DB 的資料進行彙整,成為完整的資料體

 

ViewModel

欄位定義與 View 的表單(Form)一致,與 DomainModel 可能很相似,但有些欄位只會用在表單上,所以 DomainModel 不適合用在表單 Binding 上

例如:

  • 同意上述條款
  • 舊密碼, 新密碼, 確認密碼

 

Dao

  • 回傳 DomainModel
  • 定義單純的 BD 操作 List, GetById, Save, Insert, Update
  • 與 ORM 不同,是進一步將資料操作簡化
  • 保證 Method 的通用性,避免功能導向,例如: 有一個 Method 是專門為了A畫面功能而存在的

 

Service

  • 回傳 DomainModel
  • 驗證商業邏輯,如必要資料欄位,數值範圍等...
  • 調用一個或多個 Dao 來完成商業邏輯
  • 處理 DB 交易,來協調多個 Dao 的調用

 

Controller

  • 處理 DomainModel 到 ViewModel 的轉換
  • 調用 Service 進行 DomainModel 的資料處理
  • 調配 Responses 的結果 Html, Json, PDF ...
  • 驗證資料類型的正確,數值﹑日期
  • 負責 Access 的權限阻擋

 

View

  • View 是被動的,不能存取任何 Controller / Service / Dao
  • 負責資料呈現及格式化,如 日期﹑金額 ...
  • Ajax 只能對 Controller 調用

 

附註

  1. 同一層之間不可以互相參考,這容易發生循環參考,例如:
    • Controller 呼叫 Controller
    • Service 呼叫 Service
    • Dao 呼叫 Dao
  2. 同一層之間有相同邏輯可以抽離到 Support 類
     
  3. 建議定義命名規範,讓每一個人寫出來的程式像是同一個人寫的,好處是降低支援或接手的人的困難度
     
  4. 任何第三方的 API 都需要包裝,隔離直接相依的問題,而且可以單獨測試
     

 

架構關係全貌

 

Model 傳遞的關係

 

用 Interface 隔離實作

 

Web Api 的呼叫關係

 

2022-09-05 11:29

[轉載] opencv js getPerspectiveTransform,perspectiveTransform 方法使用

轉載自: opencvjs getPerspectiveTransform,perspectiveTransform方法使用

OpenCV JavaScript版本,使用getPerspectiveTransform,PerspectiveTransform方法。

JavaScript和python版本不同的是Mat的创建方法不同,python会在内部自动把数据转换成Mat类,也JavaScript不会,所以刚开始没有找到JavaScript创建Mat方法,走了很多弯路。

这只是测试代码,没有使用项目中真实的数据,所有有一定的偏差。

如果有什么错误,欢迎纠正。

下面上代码:

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>Hello OpenCV.js</title>
</head>

<body>
  <h2>Hello OpenCV.js</h2>
  <h2>getPerspectiveTransform,PerspectiveTransform方法使用</h2>
  <p id="status">OpenCV.js is loading...</p>
  <div>
    <button onClick="myclick()">输出结果</button>
  </div>
  <script type="text/javascript">
    function myclick() {
      //代码 getPerspectiveTransform
      //创建数据
      let srcTri = cv.matFromArray(4, 1, cv.CV_32FC2, [56, 65, 368, 52, 28, 387, 389, 390]);
      let dstTri = cv.matFromArray(4, 1, cv.CV_32FC2, [0, 0, 300, 0, 0, 300, 300, 300]);
      //转换的数据
      let M = cv.getPerspectiveTransform(srcTri, dstTri);
      console.log("getPerspectiveTransform M", M);

      //==== PerspectiveTransform ======
      //point点的数据一定要是一维的,opencv会自己去处理
      let points = [
        1, 2,
        3, 4,
        5, 6,
        7, 8
      ];
      //原数据
      points = cv.matFromArray(4,1,cv.CV_32FC2,points);
      //转换后的数据
      let points_trans = new cv.Mat();
      cv.perspectiveTransform(points, points_trans,M);
      console.log("points", points);
      console.log("points_trans", points_trans);

    }
    function onOpenCvReady() {
      document.getElementById('status').innerHTML = 'OpenCV.js is ready.';

    }
  </script>
  <script async src="https://docs.opencv.org/4.x/opencv.js" onload="onOpenCvReady();" type="text/javascript"></script>
  <script src="https://docs.opencv.org/4.x/utils.js" type="text/javascript"></script>
</body>

</html>

getPerspectiveTransform结果

perspectiveTransform结果

2022-08-01 11:37

[轉載] NLog Variables

轉載自:當條件等于不作業時.net核心Nlog過濾器

我必須從我的啟動類傳遞變數值

LogManager.Configuration.Variables["environment"] = "Development";

我在我的 nlog.config 檔案中添加了以下過濾器

<rules>
    <logger name="*" minlevel="Error" writeTo="logfile">
        <filters>
            <when condition="equals('${var:environment}', 'Development')" action="Ignore" />
        </filters>
    </logger>
</rules>

即使我將值作為 Development 傳遞,該訊息仍會被記錄而不是忽略。

但是,當我對它的作業值進行硬編碼時

您在 NLog中發現了一個錯誤,但如果您這樣做,它應該可以作業(也會更快):

<rules>
    <logger name="*" minlevel="Error" writeTo="logfile">
        <filters defaultAction='log'>
            <when condition="'${var:environment}' == 'Development'" action="Ignore" />
        </filters>
    </logger>
</rules>

您也可以用 minLevel="${var:EnvironmentMinLevel:whenEmpty=Error}" 處理,這比 <filters> 快得多

<rules>
    <logger name="*" minlevel="${var:EnvironmentMinLevel:whenEmpty=Error}" writeTo="logfile" />
</rules>

設定 Variables 要記得呼叫 Reconfig,或者在 config 中設定 autoReload="true"

NLog.LogManager.Configuration.Variables["EnvironmentMinLevel"] = "Off";
NLog.LogManager.ReconfigExistingLoggers();

另請參閱 https://github.com/NLog/NLog/wiki/Filtering-log-messages#semi-dynamic-routing-rules