app.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  1. from flask import Flask, render_template, request, jsonify
  2. import psycopg2
  3. from psycopg2.extras import DictCursor
  4. import logging
  5. import ollama
  6. import json
  7. import datetime
  8. import uuid
  9. import os
  10. from vocal import voice_text
  11. from voice_translation_test import vocal_text
  12. from flask_cors import CORS
  13. from dotenv import load_dotenv
  14. from embed import embed
  15. from query import query
  16. from get_vector_db import get_vector_db
  17. import time
  18. from funasr import AutoModel
  19. from modelscope.pipelines import pipeline
  20. from modelscope.utils.constant import Tasks
  21. from langchain_community.chat_models import ChatOllama
  22. from langchain.prompts import ChatPromptTemplate, PromptTemplate
  23. from langchain_core.output_parsers import StrOutputParser
  24. from langchain_core.runnables import RunnablePassthrough
  25. from langchain.retrievers.multi_query import MultiQueryRetriever
  26. from get_vector_db import get_vector_db
  27. from pypinyin import lazy_pinyin
  28. import re
  29. LLM_MODEL = os.getenv('LLM_MODEL', 'qwen2:7b')
  30. load_dotenv()
  31. TEMP_FOLDER = os.getenv('TEMP_FOLDER', './_temp')
  32. os.makedirs(TEMP_FOLDER, exist_ok=True)
  33. app = Flask(__name__)
  34. CORS(app)
  35. # 配置日志
  36. logging.basicConfig(level=logging.INFO)
  37. logger = logging.getLogger(__name__)
  38. # 连接数据库
  39. conn = psycopg2.connect(
  40. dbname="real3d",
  41. user="postgres",
  42. password="postgis",
  43. # host="192.168.100.30",
  44. host="192.168.60.2",
  45. port="5432"
  46. )
  47. # Function to get the prompt templates for generating alternative questions and answering based on context
  48. def get_prompt():
  49. QUERY_PROMPT = PromptTemplate(
  50. input_variables=["question"],
  51. template="""你是一名AI语言模型助理。你的任务是生成五个
  52. 从中检索相关文档的给定用户问题的不同版本
  53. 矢量数据库。通过对用户问题生成多个视角
  54. 目标是帮助用户克服基于距离的一些局限性
  55. 相似性搜索。请提供这些用换行符分隔的备选问题。
  56. Original question: {question}""",
  57. )
  58. template = """仅根据以下上下文用中文回答问题:
  59. {context},请严格以markdown格式输出并保障寄送格式正确无误,
  60. Question: {question}
  61. """
  62. # Question: {question}
  63. prompt = ChatPromptTemplate.from_template(template)
  64. return QUERY_PROMPT, prompt
  65. # 文件保存路径
  66. UPLOAD_FOLDER = 'data/audio'
  67. os.makedirs(UPLOAD_FOLDER, exist_ok=True)
  68. #预加载模型权重到内存加快模型转文本速度
  69. #模型1
  70. model = AutoModel(model="E:\\yuyin_model\\Voice_translation", model_revision="v2.0.4",
  71. vad_model="E:\\yuyin_model\\Endpoint_detection", vad_model_revision="v2.0.4",
  72. punc_model="E:\\yuyin_model\\Ct_punc", punc_model_revision="v2.0.4",
  73. use_cuda=True,use_fast = True,
  74. )
  75. #模型2
  76. # inference_pipeline = pipeline(
  77. # task=Tasks.auto_speech_recognition,
  78. # # model='iic/speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-pytorch',
  79. # model='C:\\Users\\siwei\\.cache\\modelscope\\hub\\iic\\speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-pytorch',
  80. # # model="model\punc_ct-transformer_cn-en-common-vocab471067-large",
  81. # model_revision="v2.0.4",
  82. # device='gpu')
  83. # 后台接口
  84. @app.route('/embed', methods=['POST'])
  85. def route_embed():
  86. start_time = time.time()
  87. if 'file' not in request.files:
  88. return jsonify({"error": "No file part"}), 400
  89. file = request.files['file']
  90. if file.filename == '':
  91. return jsonify({"error": "No selected file"}), 400
  92. embedded = embed(file)
  93. end_time = time.time()
  94. print("Time taken for embedding: ", end_time - start_time)
  95. if embedded:
  96. return jsonify({"message": "File embedded successfully"}), 200
  97. return jsonify({"error": "File embedded unsuccessfully"}), 400
  98. def route_query(msg):
  99. response = query(msg)
  100. # print(response)
  101. # if response:
  102. # resObj = {}
  103. # resObj["data"] = response
  104. # resObj["code"] = 200
  105. # resObj["type"] = "answer"
  106. # return resObj
  107. # return {"error": "Something went wrong"}, 400
  108. return response
  109. @app.route('/delete', methods=['DELETE'])
  110. def route_delete():
  111. db = get_vector_db()
  112. db.delete_collection()
  113. return jsonify({"message": "Collection deleted successfully"}), 200
  114. @app.route("/")
  115. def home():
  116. return render_template('index.html')
  117. # 后台接口
  118. #定义需要替换的词
  119. target_word = "抱坡"
  120. target_word_pinyin = lazy_pinyin(target_word)
  121. #判断拼音是否相同
  122. def is_same_pinyin(word1,word2):
  123. return lazy_pinyin(word1) == lazy_pinyin(word2)
  124. #替换同音字
  125. def replace_word(text,target_word):
  126. words = re.findall(r'\b\w+\b', text)
  127. for word in words:
  128. if is_same_pinyin(word,target_word):
  129. text = text.replace(word,target_word)
  130. return text
  131. # 文件上传
  132. @app.route('/upload', methods=['POST'])
  133. def upload_file():
  134. if 'file' not in request.files:
  135. return jsonify({"error": "No file part in the request"}), 400
  136. file = request.files['file']
  137. if file.filename == '':
  138. return jsonify({"error": "No file selected for uploading"}), 400
  139. # 生成UUID文件名
  140. file_ext = os.path.splitext(file.filename)[1]
  141. filename = f"{uuid.uuid4()}{file_ext}"
  142. # 保存文件
  143. file_path = os.path.join(UPLOAD_FOLDER, filename)
  144. file.save(file_path)
  145. #语音转文字模型1
  146. res = model.generate(file_path,
  147. batch_size_s=30,
  148. hotword='test')
  149. texts = [item['text'] for item in res]
  150. msg = ' '.join(texts)
  151. # print(msg)
  152. #语音转文字模型2
  153. # res = inference_pipeline(file_path)
  154. # # print(res)
  155. # texts = [item['text'] for item in res]
  156. # # print(texts)
  157. # msg = ' '.join(texts)
  158. # msg = vocal_text(file_path)
  159. os.remove(file_path)
  160. msg = replace_word(msg,target_word)
  161. words_to_replace = ["爆破", "爆坡","高坡"]
  162. for word in words_to_replace:
  163. msg = msg.replace(word, "抱坡")
  164. print(msg)
  165. return jsonify({"msg": "上传成功",
  166. "code": 200,
  167. "filename": filename,
  168. "voiceMsg": msg
  169. }), 200
  170. # 接收消息,大模型解析
  171. chat_history = "用户:你好,我是智能助手,请问有什么可以帮助您?\\n智能助手:好的,请问您有什么需求?"
  172. sys_xuanzhi = """请扮演文本提取工具,根据输入和聊天上下文信息,基于以下因子选择、选址范围和用地类型提取这句话中的关键信息,提取到的结果请严格以json格式字符串输出并保障寄送格式正确无误,
  173. 选址范围 = ['抱坡区','天涯区','崖州区','海棠区','吉阳区' ],
  174. 因子选择 = ["高程","坡度","永久基本农田","城镇开发边界内","生态保护红线","文化保护区","自然保护地","风景名胜区","国有使用权","河道管理线","水库","公益林","火葬场","垃圾处理场","污水处理场","高压线","变电站","古树","城市道路","主要出入口","文化活动设施","体育运动场所","排水","供水","燃气","电力","电信","十五分钟社区生活圈邻里中心","社区服务设施","零售商业场所","医疗卫生设施","幼儿园服务半径","小学服务半径","为老服务设施"],
  175. 用地类型 = ['园地','耕地','林地','草地','湿地','公共卫生用地','老年人社会福利用地','儿童社会福利用地','残疾人社会福利用地','其他社会福利用地','零售商业用地','批发市场用地','餐饮用地','旅馆用地','公用设施营业网点用地','娱乐用地','康体用地','一类工业用地','二类工业用地','广播电视设施用地','环卫用地','消防用地','干渠','水工设施用地','其他公用设施用地','公园绿地','防护绿地','广场用地','军事设施用地','使领馆用地','宗教用地','文物古迹用地','监教场所用地','殡葬用地','其他特殊用地','河流水面','湖泊水面','水库水面','坑塘水面','沟渠','冰川及常年积雪','渔业基础设施用海','增养殖用海','捕捞海域','工业用海','盐田用海','固体矿产用海','油气用海','可再生能源用海','海底电缆管道用海','港口用海','农业设施建设用地','工矿用地','畜禽养殖设施建设用地','水产养殖设施建设用地','城镇住宅用地','特殊用地','居住用地','绿地与开敞空间用地','水田','水浇地','旱地','果园','茶园','橡胶园','其他园地','乔木林地','竹林地','城镇社区服务设施用地','农村宅基地','农村社区服务设施用地','机关团体用地','科研用地','文化用地','教育用地','体育用地','医疗卫生用地','社会福利用地','商业用地','商务金融用地','二类农村宅基地','图书与展览用地','文化活动用地','高等教育用地','中等职业教育用地','体育训练用地','其他交通设施用地','供水用地','排水用地','供电用地','供燃气用地','供热用地','通信用地','邮政用地','医院用地','基层医疗卫生设施用地','田间道','盐碱地','沙地','裸土地','裸岩石砾地','村道用地','村庄内部道路用地','公共管理与公共服务用地','仓储用地','交通运输用地','公用设施用地','交通运输用海','航运用海','路桥隧道用海','风景旅游用海','文体休闲娱乐用海','军事用海','其他特殊用海','空闲地','田坎','港口码头用地','管道运输用地','城市轨道交通用地','城镇道路用地','交通场站用地','一类城镇住宅用地','二类城镇住宅用地','三类城镇住宅用地','一类农村宅基地','商业服务业用地','三类工业用地','一类物流仓储用地','二类物流仓储用地','三类物流仓储用地','盐田','对外交通场站用地','公共交通场站用地','社会停车场用地','中小学用地','幼儿园用地','其他教育用地','体育场馆用地','灌木林地','其他林地','天然牧草地','人工牧草地','其他草地','森林沼泽','灌丛沼泽','沼泽草地','其他沼泽地','沿海滩涂','内陆滩涂','红树林地','乡村道路用地','种植设施建设用地','娱乐康体用地','其他商业服务业用地','工业用地','采矿用地','物流仓储用地','储备库用地','铁路用地','公路用地','机场用地'],
  176. landType是用地类型
  177. districtName是选址范围
  178. area是用地大小,单位统一转换为亩
  179. factors是因子选择
  180. 其他公里、千米的单位转换为米
  181. 输出json格式数据如下:
  182. {
  183.     "districtName": "抱坡区",
  184. "landType": "居住用地",
  185.     "area": {
  186.         "min": 30,
  187.         "max": 50
  188.     },
  189.     "factors": [
  190.         {
  191.             "type": "医疗卫生设施",
  192.             "condition": "lt",
  193.             "value": "500"
  194.         },
  195.         {
  196.             "type": "永久基本农田",
  197.             "condition": "not_intersect"
  198.         },
  199.         {
  200.             "type": "火葬场",
  201.             "condition": "gt",
  202.             "value": "1000"
  203.         },
  204. {
  205.             "type": "幼儿园服务半径",
  206.             "condition": "lt",
  207.             "value": "1000"
  208.         },
  209. {
  210.             "type": "小学服务半径",
  211.             "condition": "lt",
  212.             "value": "1000"
  213.         },
  214.     ]
  215. }
  216. json中"condition"的值为"gt"、"lt"、"get"、"let"、"between","not_intersect"、"intersect"、"not_contain"、"contain"、"between"
  217. """
  218. sys_question = """请扮演问答工具,对用户输入信息进行回答,请严格以markdown格式输出并保障寄送格式正确无误"""
  219. # 智能选址
  220. def update_chat_history(user_message):
  221. global chat_history # 使用全局变量以便更新
  222. prompt = chat_history + "\\n用户:" + user_message
  223. # 生成回复,并加入聊天上下文
  224. res = ollama.generate(
  225. model="qwen2.5:7b",
  226. stream=False,
  227. system=sys_xuanzhi,
  228. prompt=prompt,
  229. options={"temperature": 0, "num_ctx": 32000, },
  230. keep_alive=-1
  231. )
  232. # 获取机器人回复
  233. bot_message = res["response"]
  234. # 更新聊天历史
  235. chat_history += "\\n智能助手:" + bot_message
  236. # 返回机器人的回复
  237. return bot_message
  238. #简单知识问答,未关联本地知识库
  239. def update_chat_history_simple(user_message):
  240. global chat_history # 使用全局变量以便更新
  241. prompt = chat_history + "\\n用户:" + user_message
  242. # 生成回复,并加入聊天上下文
  243. res = ollama.generate(
  244. model="qwen2.5:7b",
  245. stream=False,
  246. system=sys_question,
  247. prompt=prompt,
  248. options={"temperature": 0, "num_ctx": 32000, },
  249. keep_alive=-1
  250. )
  251. # 获取机器人回复
  252. bot_message = res["response"]
  253. # 更新聊天历史
  254. chat_history += "\\n智能助手:" + bot_message
  255. # 返回机器人的回复
  256. return bot_message
  257. @app.route('/closeMsg', methods=['DELETE'])
  258. def delMsg():
  259. global chat_history
  260. chat_history = ""
  261. return jsonify({"msg": "清除成功",
  262. "code": 200,
  263. "chat_history": chat_history,
  264. })
  265. @app.route('/msg', methods=['POST'])
  266. def inputMsg():
  267. # 从请求中获取JSON数据
  268. data = request.get_json()
  269. # 检查是否接收到数据
  270. if not data:
  271. return jsonify({"error": "No data received"}), 400
  272. # 打印接收到的消息
  273. print(data['msg'])
  274. msg = data['msg']
  275. msg = replace_word(msg,target_word)
  276. words_to_replace1 = ["爆破", "爆坡"]
  277. for word in words_to_replace1:
  278. msg = msg.replace(word, "抱坡")
  279. print(msg)
  280. type = data['type']
  281. if type == 'selectLand':
  282. # 调用大模型解析
  283. # 这里调用大模型,并返回解析结果
  284. # 示例:用户输入一条消息
  285. # msg= "我计划在抱坡区选取适宜地块作为工业用地,要求其在城市开发边界内,离小学大于1000m,坡度小于25度,用地面积在80-100亩之间。"
  286. res = update_chat_history(msg)
  287. print(res) # 打印生成的回复
  288. addtress = ['抱坡区', '天涯区', '崖州区', '海棠区', '吉阳区']
  289. land = ['园地','耕地','林地','草地','湿地','公共卫生用地','老年人社会福利用地','儿童社会福利用地','残疾人社会福利用地','其他社会福利用地','零售商业用地','批发市场用地','餐饮用地','旅馆用地','公用设施营业网点用地','娱乐用地','康体用地','一类工业用地','二类工业用地','广播电视设施用地','环卫用地','消防用地','干渠','水工设施用地','其他公用设施用地','公园绿地','防护绿地','广场用地','军事设施用地','使领馆用地','宗教用地','文物古迹用地','监教场所用地','殡葬用地','其他特殊用地','河流水面','湖泊水面','水库水面','坑塘水面','沟渠','冰川及常年积雪','渔业基础设施用海','增养殖用海','捕捞海域','工业用海','盐田用海','固体矿产用海','油气用海','可再生能源用海','海底电缆管道用海','港口用海','农业设施建设用地','工矿用地','畜禽养殖设施建设用地','水产养殖设施建设用地','城镇住宅用地','特殊用地','居住用地','绿地与开敞空间用地','水田','水浇地','旱地','果园','茶园','橡胶园','其他园地','乔木林地','竹林地','城镇社区服务设施用地','农村宅基地','农村社区服务设施用地','机关团体用地','科研用地','文化用地','教育用地','体育用地','医疗卫生用地','社会福利用地','商业用地','商务金融用地','二类农村宅基地','图书与展览用地','文化活动用地','高等教育用地','中等职业教育用地','体育训练用地','其他交通设施用地','供水用地','排水用地','供电用地','供燃气用地','供热用地','通信用地','邮政用地','医院用地','基层医疗卫生设施用地','田间道','盐碱地','沙地','裸土地','裸岩石砾地','村道用地','村庄内部道路用地','公共管理与公共服务用地','仓储用地','交通运输用地','公用设施用地','交通运输用海','航运用海','路桥隧道用海','风景旅游用海','文体休闲娱乐用海','军事用海','其他特殊用海','空闲地','田坎','港口码头用地','管道运输用地','城市轨道交通用地','城镇道路用地','交通场站用地','一类城镇住宅用地','二类城镇住宅用地','三类城镇住宅用地','一类农村宅基地','商业服务业用地','三类工业用地','一类物流仓储用地','二类物流仓储用地','三类物流仓储用地','盐田','对外交通场站用地','公共交通场站用地','社会停车场用地','中小学用地','幼儿园用地','其他教育用地','体育场馆用地','灌木林地','其他林地','天然牧草地','人工牧草地','其他草地','森林沼泽','灌丛沼泽','沼泽草地','其他沼泽地','沿海滩涂','内陆滩涂','红树林地','乡村道路用地','种植设施建设用地','娱乐康体用地','其他商业服务业用地','工业用地','采矿用地','物流仓储用地','储备库用地','铁路用地','公路用地','机场用地']
  290. json_res = res.replace("json","")
  291. json_res = json_res.replace("```","")
  292. if json_res != "未找到相关数据":
  293. try:
  294. json_res = json.loads(json_res)
  295. districtName = json_res["districtName"]
  296. landType = json_res["landType"]
  297. # if landType != "未找到相关数据" and landType != "" and districtName != "未找到相关数据"and districtName != "":
  298. if landType in land and districtName in addtress:
  299. json_res = jsonResToDict(json_res)
  300. # print(json_res)
  301. else:
  302. json_res = "未找到相关数据"
  303. json_res = jsonResToDict_wrong(json_res)
  304. except:
  305. json_res = "未找到相关数据"
  306. json_res = jsonResToDict_wrong(json_res)
  307. else:
  308. json_res = "未找到相关数据"
  309. json_res = jsonResToDict_wrong(json_res)
  310. elif type == 'answer':
  311. # json_res = route_query(msg)
  312. # json_res = jsonResToDict_questions(json_res)
  313. # print(json_res) # 打印生成的回复
  314. json_res = update_chat_history_simple(msg)
  315. json_res = jsonResToDict_questions(json_res)
  316. print(json_res) # 打印生成的回复
  317. # 返回响应
  318. return jsonify(json_res)
  319. # 将大模型解析的结果转换为选址需要的数据格式
  320. def jsonResToDict(json_res):
  321. # 1.查询选址范围信息
  322. districtName = json_res["districtName"]
  323. ewkt = getAiDistrict(districtName)
  324. # 2.保存选址范围信息
  325. geomId = saveGeom(ewkt)
  326. # 3.获取用地类型信息
  327. landType = json_res["landType"]
  328. landType = getLandType(landType, "YDYHFLDM")
  329. # 4.获取模板信息
  330. factorTemplates = getTemplateByCode(landType)
  331. # TODO 以哪个因子列表为准,模版和因子个数怎么匹配
  332. now = datetime.datetime.now()
  333. formatted_time = now.strftime("%Y%m%d%H%M%S")
  334. res = {
  335. "xzmj": 1500,
  336. "xmmc": "规划选址项目_"+formatted_time,
  337. "jsdw": "建设单位",
  338. "ydxz_bsm": landType,
  339. "ydmjbegin": json_res["area"]["min"],
  340. "ydmjend": json_res["area"]["max"],
  341. "geomId": geomId,
  342. "yxyz": [],
  343. # TODO: 循环遍历
  344. # "yxyz": [
  345. # {
  346. # "id": "259e5bbaab434dbfb9c679bd44d4bfa4",
  347. # "name": "幼儿园服务半径",
  348. # "bsm": "TB_YEY",
  349. # "conditionInfo": {
  350. # "spatial_type": "distance",
  351. # "default": "lt",
  352. # "hasValue": true,
  353. # "defaultValue": "300",
  354. # "unit": "米",
  355. # "clip": false
  356. # }
  357. # }
  358. # ],
  359. # "useMultiple": json_res["useMultiple"],
  360. "useLandType": True,
  361. # "multipleDistance": json_res["multipleDistance"]
  362. }
  363. # 循环遍历输入因子
  364. factors = json_res["factors"]
  365. input_factors = {}
  366. for factor in factors:
  367. factorInfo = getFactorByName(factor["type"])
  368. if factorInfo == None:
  369. continue
  370. factorId = factorInfo["id"]
  371. factorBsm = factorInfo["bsm"]
  372. conditionInfo = factorInfo["condition_info"]
  373. conditionObj = json.loads(conditionInfo)
  374. defaultValue = '0'
  375. default = 'lt'
  376. if "value" in factor:
  377. defaultValue = str(factor["value"])
  378. if "condition" in factor:
  379. default = factor["condition"]
  380. # if defaultValue == '':
  381. # defaultValue = '0'
  382. factor_info = {
  383. "id": factorId,
  384. "name": factor["type"],
  385. "bsm": factorBsm,
  386. "conditionInfo": {
  387. "spatial_type": conditionObj["spatial_type"],
  388. "default": default,
  389. "hasValue": conditionObj["hasValue"],
  390. "defaultValue": defaultValue,
  391. "unit": conditionObj["unit"],
  392. "clip": conditionObj["clip"]
  393. }
  394. }
  395. input_factors[factor_info["id"]] = factor_info
  396. # 循环遍历模板
  397. # 记录已经添加的因子 ID
  398. added_factor_ids = set()
  399. # 首先处理模板
  400. for factorTemplate in factorTemplates:
  401. factorId = factorTemplate["id"]
  402. factorTemplate["conditionInfo"] = json.loads(factorTemplate["conditionInfo"])
  403. res["yxyz"].append(factorTemplate)
  404. added_factor_ids.add(factorId) # 记录已添加的因子 ID
  405. # 然后检查 input_factors 并添加未在模板中的因子
  406. for factor_id, factor_info in input_factors.items():
  407. if factor_id not in added_factor_ids:
  408. res["yxyz"].append(factor_info)
  409. resObj = {}
  410. resObj["data"] = res
  411. resObj["code"] = 200
  412. resObj["type"] = "selectLand"
  413. return resObj
  414. #返回问答信息
  415. def jsonResToDict_questions(json_res):
  416. resObj = {}
  417. resObj["data"] = json_res
  418. resObj["code"] = 200
  419. resObj["type"] = "answer"
  420. return resObj
  421. # 返回错误信息
  422. def jsonResToDict_wrong(json_res):
  423. resObj = {}
  424. resObj["data"] = json_res
  425. resObj["code"] = 500
  426. resObj["type"] = "selectLand"
  427. return resObj
  428. # 获取因子信息
  429. def getFactorByName(name):
  430. with conn.cursor(cursor_factory=DictCursor) as cur:
  431. sql = "SELECT * FROM base.t_fzss_fzxz_factor WHERE name = %s"
  432. complete_sql = cur.mogrify(sql, (name,)).decode('utf-8')
  433. logger.info(f"Executing SQL: {complete_sql}")
  434. cur.execute(sql, (name,))
  435. res = cur.fetchone()
  436. return res
  437. # 获取内置模板信息
  438. def getTemplateByCode(code):
  439. with conn.cursor(cursor_factory=DictCursor) as cur:
  440. sql = 'SELECT factor_id as id,factor_name as name,factor_bsm as bsm,condition_info as "conditionInfo" FROM base.t_fzss_fzxz_factor_temp WHERE land_type_code = %s'
  441. complete_sql = cur.mogrify(sql, (code,)).decode('utf-8')
  442. logger.info(f"Executing SQL: {complete_sql}")
  443. cur.execute(sql, (code,))
  444. res = cur.fetchall()
  445. # 将查询结果转换为字典列表
  446. result_list = [dict(row) for row in res]
  447. return result_list
  448. # 获取选址范围信息
  449. def getAiDistrict(name):
  450. with conn.cursor(cursor_factory=DictCursor) as cur:
  451. sql = "SELECT public.st_asewkt(geom) as geom FROM base.t_fzss_fzxz_ai_district WHERE name = %s"
  452. complete_sql = cur.mogrify(sql, (name,)).decode('utf-8')
  453. logger.info(f"Executing SQL: {complete_sql}")
  454. cur.execute(sql, (name,))
  455. res = cur.fetchone()
  456. return res["geom"]
  457. # 保存选址范围信息
  458. def saveGeom(ewkt):
  459. new_uuid = str(uuid.uuid4()) # 生成一个新的 UUID
  460. from_type = 3
  461. with conn.cursor() as cur:
  462. sql = "INSERT INTO base.t_fzss_zhxz_file(id,geom,from_type,create_time,area) VALUES (%s,public.st_geomfromewkt(%s),%s,now(),public.st_area(public.st_geomfromewkt(%s)::public.geography))"
  463. complete_sql = cur.mogrify(
  464. sql, (new_uuid, ewkt, from_type, ewkt)).decode('utf-8')
  465. logger.info(f"Executing SQL: {complete_sql}")
  466. cur.execute(sql, (new_uuid, ewkt, from_type, ewkt))
  467. conn.commit()
  468. return new_uuid
  469. # 获取用地类型信息
  470. def getLandType(landName, fzbs):
  471. with conn.cursor(cursor_factory=DictCursor) as cur:
  472. sql = "SELECT dm,mc,fzbs FROM base.t_fzss_fzxz_dict WHERE mc = %s and fzbs=%s"
  473. complete_sql = cur.mogrify(sql, (landName, fzbs)).decode('utf-8')
  474. logger.info(f"Executing SQL: {complete_sql}")
  475. cur.execute(sql, (landName, fzbs))
  476. res = cur.fetchone()
  477. return res["dm"]
  478. # getTemplateByCode("08")
  479. # getAiDistrict("抱坡区")
  480. # ewkt="SRID=4326;POLYGON ((109.568515723151 18.2729002407864, 109.564270326708 18.2607742953866, 109.580087492139 18.2571512198688, 109.588461804591 18.2570597503377, 109.58884305979 18.2645363088176, 109.582107142538 18.2732736518031, 109.568515723151 18.2729002407864))"
  481. # saveGeom(ewkt)
  482. # getFactorByName("幼儿园服务半径")
  483. # msg=voice_text('data/audio/1364627f-5a9b-42d7-b7f6-b99c094606cd.mp3')
  484. # msg=vocal_text('data/audio/1364627f-5a9b-42d7-b7f6-b99c094606cd.mp3')
  485. # print(msg)
  486. if __name__ == '__main__':
  487. # app.run()
  488. app.run(
  489. host='0.0.0.0',
  490. port=4000
  491. )