#coding=utf-8
from jspt_data import jspt_cx_table,jspt_cx_table_sql
from jspt_forms import Login, RegisterForm
from flask_sqlalchemy import SQLAlchemy
from flask import Flask, render_template, session, redirect, url_for, flash, request, jsonify
# import os
# from flask_script import Manager, Shell
import records
from flask_login import UserMixin, LoginManager, login_required, login_user, logout_user, current_user
from werkzeug.security import   generate_password_hash,check_password_hash
# from sklearn.metrics import f1_score    调试镜像去掉这个包
from thestr import strrelease
import pandas as pd
from datetime import datetime
import cv2
from paddleocr import PaddleOCR
import time
import math
# import requests
# import json
import base64
import numpy as np
import io
import sys
from fuzzywuzzy import fuzz
import gc


sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='utf-8')   #解决print中文报错问题
app = Flask(__name__)
app.config['TEMPLATES_AUTO_RELOAD']=True
app.config['SECRET_KEY'] = 'dirneciemedmdua;APE;D'
strpg=f"//qzcx:{strrelease(99,'DDEAMDBFDFBFCFCFDFBFDF')}@134.140.1.157:5432/qzdatadb"
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.session_protection = 'basic'
# session_protection设置登录用户的安全级别,可选None、'basic'、'strong',安全等级由低到高。
# 最高的安全等级‘strong’,flask_login会记住客户端的IP地址和用户代理信息,如果发现有不一致就登出用户
login_manager.login_view = 'jspt_login'
#在用户未登录的情况下,输入http://localhost:5000/loginrq时,flask会提示我们先进行登录
#这是 初始化flask_login时要告诉它登录的视图函数的原因
login_manager.login_message = u"请先登录。"
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql:'+strpg
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
db = SQLAlchemy(app)
app.debug=True
v_yure = 3120
v_picsize=1.5

ocr = PaddleOCR(use_angle_cls=True, use_gpu=True, lang="ch", det_limit_side_len=320, det_limit_type="min")

class User(UserMixin, db.Model):
    __tablename__ = 'jspt_user'
    user_id = db.Column(db.Integer, primary_key=True)
    user_name = db.Column(db.String(30))
    _password = db.Column(db.String(200))
    user_role = db.Column(db.String(30))

    def __init__(self, user_id, user_name, password, user_role=''):
        self.user_id = user_id
        self.user_name = user_name
        self.password = password
        self.user_role = user_role

    @property
    def password(self):
        return self._password

    @password.setter
    def password(self, raw):
        self._password = generate_password_hash(raw)

    def check_password(self, raw):
        return check_password_hash(self._password, raw)


    def get_id(self):
        return self.user_id

    def __repr__(self):
        return '<User %r>' % self.user_name

@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))


@app.route('/', methods=['GET', 'POST'])
def index():
    return render_template('jpg_upload.html')


@app.route('/jspt_upload_file/', methods=['POST'])
@login_required
def jspt_upload_file():
      print('jspt_upload_file POST')
      db = records.Database('postgresql+psycopg2:' + strpg)
      print('连接成功')
      rows = db.query(f"select b.*,b.up_count-a.cou lastcou from (select count(*) cou from jspt_upload_record a where user_id={current_user.user_id} )a,(select j.*,to_char(end_time,'yyyy-mm-dd hh24:mi:ss') time3 from jspt_setting j where type='开放')b ")
      s_endtime=rows[0].as_dict()['time3']
      endtime=datetime.strptime(s_endtime,'%Y-%m-%d %H:%M:%S')
      print(endtime)
      if endtime<datetime.now():
          db.close()
          return jsonify({
              'code': 0,
              'msg': f"您上传时间已超过赛题最后上传时间:{endtime},无法再次上传"
          })
      lastcou=rows[0].as_dict()['lastcou']
      if lastcou<=0:
          db.close()
          return jsonify({
              'code': 0,
              'msg': f"您上传次数已超{rows[0].as_dict()['up_count']}次,无法再次上传"
          })
      f = request.files.get('file')
      df2 = pd.read_csv(f, encoding='utf-8')
      df2 = df2.sort_values('id')
      f.save('./upload_csv/'+f.filename)
      print('保存上传文件成功')

      df1 = pd.read_csv(f"./static/{rows[0].as_dict()['da']}.csv", encoding='utf-8')
      df1 = df1.sort_values('id')
      if rows[0].as_dict()['scoretype']=='二分类':
            print('二分类评分')
            # score = f1_score(df1['lable'].tolist(), df2['lable'].tolist(), average="binary")
            score =95
      else:
            print('多分类评分')
            # score = f1_score(df1['lable'].tolist(), df2['lable'].tolist(), average="macro")
            score =95
      df1.drop(df1.index, inplace=True)
      df2.drop(df2.index, inplace=True)
      s_sql = f"insert into jspt_upload_record(user_id, upload_time,filename,score) values({current_user.user_id},now(),'{f.filename}',{score})"
      print(s_sql)
      db.bulk_query(s_sql)
      rows = db.query(f'SELECT max(score) score FROM jspt_score where user_id={ current_user.user_id }')
      if rows[0].as_dict()['score']:
          print('上载过')
          if rows[0].as_dict()['score']<score:
                print('上载过,但这次分数比以前高')
                s_sql = f"delete from  jspt_score where user_id={current_user.user_id}"
                db.bulk_query(s_sql)
                s_sql = f"insert into jspt_score(user_id, score,up_time) values({current_user.user_id},{score},now())"
                db.bulk_query(s_sql)
          else:
                print('上载过,这次分数没以前高')
      else:
          print('未上载过')
          s_sql = f"insert into jspt_score(user_id, score,up_time) values({current_user.user_id},{score},now())"
          db.bulk_query(s_sql)
      print('保存数据库上传记录,评分成功')
      db.close()
      return jsonify({
          'code': 0,
          'msg': f'上传成功,分数:{score},还可以上传{lastcou-1}次'
      })


def rotate_image(image, angle):
    # 获取图片尺寸
    (h, w) = image.shape[:2]
    # 旋转中心设为图片中心
    center = (w / 2, h / 2)
    # 获取旋转矩阵
    M = cv2.getRotationMatrix2D(center, angle, 1.0)
    # 执行仿射变换
    rotated = cv2.warpAffine(image, M, (w, h))
    return rotated

def myocr(img,rotate=0,picsize=1.5,out="html"):
    start_time = time.time()
    if rotate == 1:
        img = rotate_image(img, 90)
    if picsize != 1:
        y, x = img.shape[0:2]
        v_size = y
        if v_size < x:
            v_size = x  # 取大的那个
        if (v_size * picsize > v_yure):  # 如果比3120大,重新计算picsize
            picsize = math.floor((v_yure / v_size) * 10) / 10  # 小数点向下取整1位,不四舍五入
        img = cv2.resize(img, (int(x * picsize), int(y * picsize)))
    reslut = ocr.ocr(img)
    # print(reslut)
    if out=="html":
        s = '<table class="layui-table" style="width:50%">'
    else:
        list_out =[]
    if reslut:
          for line in reslut:
              for word in line:
                 text_line=word[-1]
                 text=text_line[0]
                 # print('text',text)
                 if out == "html":
                     s=s+'<td>'+text+'</td></tr>'
                 else:
                     list_out.append(text)

    end_time = time.time()
    execution_time = round(end_time - start_time,2)
    # del ocr
    # gc.collect()
    if out == "html":
        s = s + '<td>' + f"耗时: {execution_time} 秒" + '</td></tr>'
        s = s + '</table>'
        return s
    else:
        list_out.append(f"耗时:{execution_time} 秒")
        dict_out={}
        dict_out['time']= execution_time
        dict_out['str']= ' '.join(str(x) for x in list_out)
        dict_out['list']= list_out
        return dict_out


                 # file.write(text+'\n')
    # print('text:',text)

@app.route('/jpg_upload_file/', methods=['POST'])
def jpg_upload_file():
      print('jpg_upload_file POST')
      f = request.files.get('file')
      print(f.filename)
      f.save('./upload_csv/' + f.filename)
      img = cv2.imread(f"./upload_csv/{f.filename}")
      s_result=myocr(img,rotate=0,picsize=v_picsize,out='html')
      # s_result = myocr(img, rotate=0, picsize=1, out='html')
      return ({
          'code': 0,
          'msg': f'上传成功',
          'html_str': s_result
      })

@app.route('/scan_pig/', methods=[ 'GET','POST'])
def scan_pig():
    print('scan_pig')
    # myocr()
    return render_template('jpg_upload.html')



def list_append_check(list=[],append_key1='loid',append_key2='check_ok'):
    list_append=[]
    for i in list:
        dict_temp = {}
        dict_temp[append_key2] = 0
        dict_temp[append_key1] = i
        list_append.append(dict_temp)
    return list_append

def list_check_ok(list=[]):
    if len(list)>0:
        result_list1 = 0
        result_list2 = 0
        for i in list:
            if i['check_ok'] == 1:
                result_list1 = 1
            if i['check_ok'] == 0:
                result_list2 = 1
        if result_list1 == 1 :
            if  result_list2 == 1:
                result_list = 0.5
            else:
                result_list = 1
        else:
            result_list = 0
    else:
        result_list = 1
    return result_list

@app.route('/send_base64/', methods=[ 'GET','POST'])
def send_base64():
    print('route send_base64')
    print(f"ID:{ request.headers['X-APP-ID']}")
    print(f"KEY:{request.headers['X-APP-KEY']}")
    if (request.headers['X-APP-ID']=='65a65cb38f7110bae954f1550ef70c4c') and (request.headers['X-APP-KEY']=='6de19a1e5813386a24225d6223d033eu') :
        f=request.get_json()
        # print(f['imageContent'])
        list_image = f['list_image']
        list_dict_result = []
        list_code, list_loid, list_ywh = [], [], []
        if f['dict_map']['need_match'] == 1:  # =1时需要比对串码,loid等信息
            list_code = list_append_check(f['dict_map']['list_code'],append_key1='devCtei',append_key2='check_ok')
            list_loid = list_append_check(f['dict_map']['list_loid'],append_key1='loid',append_key2='check_ok')
            list_ywh = list_append_check(f['dict_map']['list_ywh'],append_key1='ywh',append_key2='check_ok')
        i_gmt = 0
        for i in range(len(list_image)):
            imgdata=base64.b64decode(list_image[i])
            nparr = np.frombuffer(imgdata, np.uint8)
            img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
            dict_result={}
            if f['rotate']==1:
                dict_result1 = myocr(img, rotate=0, picsize=f['picsize'], out='dict')
                dict_result2 = myocr(img, rotate=1, picsize=f['picsize'], out='dict')
                dict_result['list']=list(set(dict_result1['list'] + dict_result2['list']))  # 合并去重
                dict_result['str'] = ' '.join(str(x) for x in dict_result['list'])
                dict_result['time']=round(dict_result1['time'] + dict_result2['time'],2)
            else:
                dict_result = myocr(img, rotate=0, picsize=f['picsize'], out='dict')
            if f['dict_map']['need_match'] == 1:  #=1时需要比对串码,loid等信息
                ocr_f = dict_result['str']
                if ("全心全意" in ocr_f) | ("10分满意" in ocr_f) | ("宽带自助排障" in ocr_f) | ("10 分满意" in ocr_f):
                    i_gmt = 1
                for dict_loid in list_loid:
                    if 'loid' in dict_loid.keys():
                        if dict_loid['check_ok'] == 0:
                            loid = dict_loid['loid']
                            if loid in ocr_f:
                                dict_loid['check_ok'] = 1
                            elif fuzz.partial_ratio(ocr_f, loid) > 62:
                                dict_loid['check_ok'] = 1
                            elif (fuzz.partial_ratio(ocr_f[0:190], loid) > 62):
                                dict_loid['check_ok'] = 1
                            elif (fuzz.partial_ratio(ocr_f[100:290], loid) > 62):
                                dict_loid['check_ok'] = 1
                for dict_ywh in list_ywh:
                    if 'ywh' in dict_ywh.keys():
                        if dict_ywh['check_ok'] == 0:
                            ywh = dict_ywh['ywh']
                            if ywh in ocr_f:
                                dict_ywh['check_ok'] = 1
                            elif fuzz.partial_ratio(ocr_f, ywh) > 62:
                                dict_ywh['check_ok'] = 1
                            elif (fuzz.partial_ratio(ocr_f[0:190], ywh) > 62):
                                dict_ywh['check_ok'] = 1
                            elif (fuzz.partial_ratio(ocr_f[100:290], ywh) > 62):
                                dict_ywh['check_ok'] = 1
                for element in list_code:
                    if 'devCtei' in element.keys():
                        if element["check_ok"] == 0:  # 该串码需要比对该照片,如果=1表示前面已经比对过该串码,比对成功了不需要再比对该串码
                            devCtei = element["devCtei"]
                            if len(devCtei) > 22:
                                devCtei = devCtei[13:100]
                            if devCtei in ocr_f:
                                element["check_ok"] = 1  # 比对成功,设置为1
                            elif fuzz.partial_ratio(ocr_f, devCtei) > 55:
                                element["check_ok"] = 1  # 比对成功,设置为1
                            elif (fuzz.partial_ratio(ocr_f[0:190], devCtei) > 55):
                                element['check_ok'] = 1
                            elif (fuzz.partial_ratio(ocr_f[100:290], devCtei) > 55):
                                element['check_ok'] = 1
            list_dict_result.append(dict_result)
        dict_map_result = {}
        if f['dict_map']['need_match'] == 1:  # =1时需要比对串码,loid等信息
            dict_temp={}
            dict_temp['float_check_result'] =list_check_ok(list_code)
            dict_temp['list_check_detail'] = list_code
            dict_map_result['dict_code_result']=dict_temp
            dict_temp = {}
            dict_temp['float_check_result'] = list_check_ok(list_loid)
            dict_temp['list_check_detail'] = list_loid
            dict_map_result['dict_loid_result'] = dict_temp
            dict_temp = {}
            dict_temp['float_check_result'] = list_check_ok(list_ywh)
            dict_temp['list_check_detail'] = list_ywh
            dict_map_result['dict_ywh_result'] = dict_temp
            dict_map_result['i_gmt'] = i_gmt
        gc.collect()
        return ({
            'code': 0,
            'msg': f'上传成功',
            'data': list_dict_result,
            'dict_map_result' : dict_map_result
        })
    else:
        return ({
            'code': -1,
            'msg': f'APPID KEY 验证失败',
            'data': []
        })


if __name__ == '__main__':
    app.run(debug=True)
    pic0 = cv2.imread(f"./templates/big_img{v_yure}.jpg")
    result0 = ocr.ocr(pic0, cls=True)
    # print(f"./templates/big_img{v_yure}.jpg")


以下是对这段Python代码的分析:

1. 代码概述

这是一个使用Flask框架构建的Web应用程序,功能涉及用户认证、文件上传(CSV和图像文件)、图像文字识别(OCR)、数据评分以及基于OCR结果的信息比对等。

2. 导入模块

  • from jspt_data import jspt_cx_table,jspt_cx_table_sql等:从自定义模块jspt_data中导入数据相关的内容,虽然不清楚具体是什么,但可能是与数据库查询表或SQL语句相关。
  • from flask_sqlalchemy import SQLAlchemy等:导入Flask相关的扩展和库,如SQLAlchemy用于数据库操作,Flask用于创建Web应用,render_template等用于渲染模板,flask_login相关的用于用户认证。
  • import records:用于数据库查询操作,可能是一个更方便的数据库访问库。
  • import cv2、from paddleocr import PaddleOCR等:用于图像处理(cv2)和光学字符识别(PaddleOCR)。
  • import pandas as pd:用于数据处理,特别是读取和操作CSV文件。
  • from fuzzywuzzy import fuzz等:用于模糊字符串匹配。

3. 配置和初始化

  • app = Flask(__name__):创建Flask应用实例。
  • app.config设置:
    • 'TEMPLATES_AUTO_RELOAD'=True:自动重新加载模板,方便开发过程中修改模板后即时看到效果。
    • 'SECRET_KEY' = 'dirneciemedmdua;APE;D':用于加密会话等安全相关的密钥。
    • 'SQLALCHEMY_DATABASE_URI':设置数据库连接字符串,这里连接的是PostgreSQL数据库。
    • 'SQLALCHEMY_COMMIT_ON_TEARDOWN' = True:在每次请求结束后自动提交数据库事务。
  • login_manager初始化:
    • login_manager.init_app(app):将LoginManager与Flask应用关联。
    • login_manager.session_protection和login_manager.login_view设置:定义用户会话保护级别和未登录时重定向的登录视图。

4. 数据库模型 - User类

  • 继承关系:继承自UserMixin和db.Model,UserMixin提供了Flask - Login所需的用户方法,db.Model是SQLAlchemy的模型基类。
  • 属性:
    • user_id、user_name、_password、user_role:分别表示用户ID、用户名、密码(密码存储的是哈希值)和用户角色。
  • 方法:
    • password属性的getter和setter方法:getter返回密码哈希值,setter将原始密码转换为哈希值存储。
    • check_password方法:用于验证输入的原始密码与存储的哈希密码是否匹配。
    • get_id方法:返回用户ID,满足Flask - Login的要求。

5. 路由和视图函数

  • @app.route('/') - index函数:渲染jpg_upload.html模板,作为应用的根路径。
  • @app.route('/jspt_upload_file/', methods=['POST']) - jspt_upload_file函数(需要登录):
    • 功能:处理用户上传的CSV文件,进行评分并保存上传记录和评分到数据库。包括检查上传时间和剩余上传次数,根据赛题类型(二分类或多分类)计算评分(目前评分固定为95,原代码中可能使用f1_score计算)。
    • 数据库操作:连接数据库,查询相关设置信息,如上传次数限制和赛题结束时间,执行插入和删除等操作。
  • @app.route('/jpg_upload_file/', methods=['POST']) - jpg_upload_file函数:
    • 功能:处理用户上传的图像文件(JPG),调用myocr函数进行OCR识别,并返回识别结果。
  • @app.route('/scan_pig/', methods=['GET', 'POST']) - scan_pig函数:目前只是渲染jpg_upload.html模板,可能功能未完全实现。
  • @app.route('/send_base64/', methods=['GET', 'POST']) - send_base64函数:
    • 功能:接收包含图像Base64编码数据、相关配置信息(如是否需要匹配、旋转设置、图像尺寸等)的JSON请求。验证请求头中的APPID和APPKEY,对图像进行OCR识别,并根据配置进行信息比对(如串码、loid、ywh等信息),最后返回结果,包括OCR结果和比对结果。

6. 辅助函数

  • rotate_image函数:根据给定角度旋转图像。
  • myocr函数:对图像进行OCR操作,可选择旋转图像、调整图像大小,根据输出格式(html或dict)返回不同形式的结果,同时计算并返回执行时间。
  • list_append_check函数:创建一个包含指定键值对的字典列表,用于初始化比对信息的状态。
  • list_check_ok函数:根据列表中字典的check_ok值判断整体的检查结果。

7. 主程序入口

  • if __name__ == '__main__':块:运行Flask应用,开启调试模式。同时读取一个大图像文件并进行OCR操作(这部分可能是测试代码)。

8. 代码改进建议

  • 错误处理和日志记录:在数据库操作、文件读取和OCR等可能出错的地方添加更完善的错误处理和日志记录,方便排查问题。
  • 安全性增强:对于接收的文件和请求数据进行更严格的安全检查,防止潜在的安全漏洞,如文件类型检查、输入数据的合法性验证等。
  • 代码优化:减少代码中的重复逻辑,例如jspt_upload_file和send_base64中都有部分OCR和结果处理逻辑,可以考虑提取成更通用的函数。同时,可以优化数据库查询语句,避免不必要的查询和数据处理。

更多推荐