提交
This commit is contained in:
63
app/utils/bagname.py
Normal file
63
app/utils/bagname.py
Normal file
@@ -0,0 +1,63 @@
|
||||
import re
|
||||
from datetime import datetime
|
||||
from typing import Dict, Tuple
|
||||
|
||||
# 你 COS 的公共前缀可以做成默认参数,便于不同环境覆盖
|
||||
DEFAULT_COS_PREFIX = "https://data-miningc01-1318950322.cos.ap-shanghai-adc.myqcloud.com/"
|
||||
|
||||
def extract_datetime_from_filename(
|
||||
bag_filename: str,
|
||||
cos_prefix: str = DEFAULT_COS_PREFIX,
|
||||
) -> Tuple[str, Dict[str, str]]:
|
||||
"""
|
||||
从 .bag 文件名中提取采集时间,并生成对应的多视角视频 URL。
|
||||
|
||||
期望文件名包含片段:_{YYYYMMDD}-{HHMMSS}_ 或以 .bag 结尾,例如:
|
||||
PL162802_event_all_time_event_20250416-193015_0.bag
|
||||
PL123456_event_all_time_event_20250828-221018.bag
|
||||
xx_20250524-180926_.bag
|
||||
xx_20250524-180926_3.bag
|
||||
|
||||
返回:
|
||||
(datetime_full_str, video_urls)
|
||||
- datetime_full_str: 'YYYY-MM-DD HH:MM:SS'
|
||||
- video_urls: COS 上该 bag 的多视角视频 URL 字典
|
||||
|
||||
异常:
|
||||
ValueError: 当文件名不匹配预期格式时抛出
|
||||
"""
|
||||
# 提取日期时间
|
||||
match = re.search(r"_(\d{8})-(\d{6})(?:_|\.|$)", bag_filename)
|
||||
|
||||
if not match:
|
||||
raise ValueError(f"文件名格式错误,无法提取时间信息:{bag_filename}")
|
||||
|
||||
date_part, time_part = match.groups()
|
||||
year, month, day = date_part[:4], date_part[4:6], date_part[6:8]
|
||||
hour, minute, second = time_part[:2], time_part[2:4], time_part[4:6]
|
||||
|
||||
datetime_full = f"{year}-{month}-{day} {hour}:{minute}:{second}"
|
||||
|
||||
|
||||
print(f'datetime_full:{datetime_full}')
|
||||
# 生成视频路径(与现有逻辑一致)
|
||||
# 去掉 .bag 后缀,再补上 " - {View}.mp4"
|
||||
base = bag_filename[:-4] if bag_filename.endswith(".bag") else bag_filename
|
||||
view_map = {
|
||||
"wide": "Wide",
|
||||
"left": "Left",
|
||||
"right": "Right",
|
||||
"rear_left": "Rear Left",
|
||||
"rear_right": "Rear Right",
|
||||
"rear": "Rear",
|
||||
}
|
||||
video_urls = {}
|
||||
for key, label in view_map.items():
|
||||
video_final_path = f"{base}- {label}.mp4"
|
||||
# 目录层级:momenta/videos/YYYY/MM/DD/{文件名 - {View}.mp4}
|
||||
video_key = f"momenta/videos/{year}/{month}/{day}/{video_final_path}"
|
||||
video_urls[key] = f"{cos_prefix}{video_key}"
|
||||
|
||||
print(f'v{video_urls}')
|
||||
|
||||
return datetime_full, video_urls
|
||||
3301
app/utils/driving_tree.py
Normal file
3301
app/utils/driving_tree.py
Normal file
File diff suppressed because it is too large
Load Diff
120
app/utils/fst_tree.py
Normal file
120
app/utils/fst_tree.py
Normal file
@@ -0,0 +1,120 @@
|
||||
def build_tree(records):
|
||||
"""
|
||||
构建带层级属性的树形结构
|
||||
返回: 根节点字典 {id, name, children: [...], level}
|
||||
"""
|
||||
nodes = {}
|
||||
root_candidates = set() # 存储可能的根节点ID [2,7](@ref)
|
||||
|
||||
# 创建所有节点(暂不设置level)
|
||||
for id_str, name, parent_id in records:
|
||||
try:
|
||||
node_id = int(id_str)
|
||||
parent_id_val = int(parent_id) if parent_id and str(parent_id).isdigit() else None
|
||||
|
||||
nodes[node_id] = {
|
||||
'id': node_id,
|
||||
'label': name,
|
||||
'children': [] # level属性稍后通过递归设置
|
||||
}
|
||||
|
||||
# 标记根节点候选(父节点不存在或无效)
|
||||
if parent_id_val is None or parent_id_val not in nodes:
|
||||
root_candidates.add(node_id)
|
||||
except (ValueError, TypeError):
|
||||
continue
|
||||
|
||||
# 构建父子关系
|
||||
for id_str, _, parent_id in records:
|
||||
try:
|
||||
node_id = int(id_str)
|
||||
parent_id_val = int(parent_id) if parent_id and str(parent_id).isdigit() else None
|
||||
|
||||
# 连接父子节点
|
||||
if parent_id_val in nodes and node_id in nodes:
|
||||
nodes[parent_id_val]['children'].append(nodes[node_id])
|
||||
# 从根候选移除(有父节点说明不是根)
|
||||
if node_id in root_candidates:
|
||||
root_candidates.remove(node_id)
|
||||
except (ValueError, TypeError):
|
||||
continue
|
||||
|
||||
# ============ 新增核心逻辑:递归设置层级属性 ============
|
||||
def set_node_level(node, current_level):
|
||||
"""递归设置节点层级属性(DFS深度优先遍历)[3,7](@ref)"""
|
||||
node['level'] = current_level # 设置当前节点层级
|
||||
for child in node['children']:
|
||||
set_node_level(child, current_level + 1) # 子节点层级+1
|
||||
|
||||
# 为所有根节点设置层级
|
||||
for root_id in root_candidates:
|
||||
set_node_level(nodes[root_id], 0) # 根节点层级从0开始
|
||||
|
||||
# 返回第一个根节点(或None)
|
||||
return nodes[next(iter(root_candidates))] if root_candidates else None
|
||||
|
||||
|
||||
def build_sub_tree(records, target_parent_id=None):
|
||||
"""
|
||||
构建带层级属性的树形结构
|
||||
|
||||
参数:
|
||||
records: 记录列表,格式为[(id_str, name, parent_id)]
|
||||
target_parent_id: 指定的一级标签ID(作为子树根节点)
|
||||
|
||||
返回:
|
||||
带level属性的子树或完整树结构
|
||||
"""
|
||||
nodes = {}
|
||||
root_candidates = set() # 存储可能的根节点ID
|
||||
|
||||
# 创建所有节点(暂不设置level)
|
||||
for id_str, name, parent_id in records:
|
||||
try:
|
||||
node_id = int(id_str)
|
||||
parent_id_val = int(parent_id) if parent_id and str(parent_id).isdigit() else None
|
||||
|
||||
nodes[node_id] = {
|
||||
'id': node_id,
|
||||
'label': name,
|
||||
'children': [] # level属性稍后通过递归设置
|
||||
}
|
||||
|
||||
if parent_id_val is None or parent_id_val not in nodes:
|
||||
root_candidates.add(node_id)
|
||||
except (ValueError, TypeError):
|
||||
continue
|
||||
|
||||
# 构建父子关系
|
||||
for id_str, _, parent_id in records:
|
||||
try:
|
||||
node_id = int(id_str)
|
||||
parent_id_val = int(parent_id) if parent_id and str(parent_id).isdigit() else None
|
||||
|
||||
if parent_id_val in nodes and node_id in nodes:
|
||||
nodes[parent_id_val]['children'].append(nodes[node_id])
|
||||
if node_id in root_candidates:
|
||||
root_candidates.remove(node_id)
|
||||
except (ValueError, TypeError):
|
||||
continue
|
||||
|
||||
# ============ 新增核心逻辑:递归设置层级属性 ============
|
||||
def set_node_level(node, current_level):
|
||||
"""递归设置节点层级属性(DFS深度优先遍历)"""
|
||||
node['level'] = current_level # 设置当前节点层级
|
||||
for child in node['children']:
|
||||
set_node_level(child, current_level + 1) # 子节点层级+1[8](@ref)
|
||||
|
||||
# 场景1:处理子树
|
||||
if target_parent_id is not None:
|
||||
target_id = int(target_parent_id)
|
||||
subtree_root = nodes.get(target_id)
|
||||
if subtree_root:
|
||||
set_node_level(subtree_root, 1) # 子树根节点从0开始
|
||||
return subtree_root
|
||||
|
||||
# 场景2/3:处理完整树或森林
|
||||
for root_id in root_candidates:
|
||||
set_node_level(nodes[root_id], 0) # 每个根节点从0开始[7](@ref)
|
||||
|
||||
return nodes[next(iter(root_candidates))] if root_candidates else nodes
|
||||
29
app/utils/get_vlm_token.py
Normal file
29
app/utils/get_vlm_token.py
Normal file
@@ -0,0 +1,29 @@
|
||||
import requests
|
||||
|
||||
|
||||
class Demo:
|
||||
def __init__(self):
|
||||
self.api_key = "bX7pK9mR2nL4qF6s" # 用户的 API Key
|
||||
self.api_secret = "eJ8wT3hY5cA2vN9uZ1xD7fG4" # 用户的 API Secret
|
||||
self.host = "http://10.0.220.110" # 接口主地址
|
||||
self.headers = {"Content-Type": "application/json", "Authorization": ""}
|
||||
|
||||
# 获取认证token
|
||||
def getAuthToken(self):
|
||||
# print("开始登录")
|
||||
url = f"{self.host}/api/openapi/authenticate"
|
||||
data = {"apiKey": self.api_key, "apiSecret": self.api_secret}
|
||||
try:
|
||||
response = requests.post(url, json=data)
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
|
||||
if result["success"]:
|
||||
self.headers["Authorization"] = f"Bearer {result['data']['token']}"
|
||||
print("登录vlm成功")
|
||||
return result["data"]["token"]
|
||||
else:
|
||||
raise Exception(f"登录失败: {result['message']}")
|
||||
except requests.RequestException as e:
|
||||
print(f"登录请求失败: {e}")
|
||||
raise
|
||||
104
app/utils/log_record.py
Normal file
104
app/utils/log_record.py
Normal file
@@ -0,0 +1,104 @@
|
||||
from functools import wraps
|
||||
from flask import request, g
|
||||
import json
|
||||
|
||||
from flask_jwt_extended import current_user
|
||||
from app.models import OperationHistory, User
|
||||
|
||||
from app import db,jwt
|
||||
|
||||
def log_operation(func):
|
||||
@wraps(func)
|
||||
def decorated(*args, **kwargs):
|
||||
log_entry = OperationHistory(
|
||||
api_path=request.path,
|
||||
http_method=request.method,
|
||||
ip_address=request.remote_addr,
|
||||
request_params=None
|
||||
# 用户信息暂留空
|
||||
)
|
||||
# 记录GET参数
|
||||
if request.method == "GET":
|
||||
log_entry.request_params = request.args.to_dict()
|
||||
|
||||
# 记录POST参数(支持JSON/表单/原始数据)
|
||||
elif request.method == "POST":
|
||||
if request.is_json:
|
||||
res=request.get_json(silent=True) or None
|
||||
json_str = json.dumps(res,ensure_ascii=False)
|
||||
log_entry.request_params = json_str
|
||||
elif request.form:
|
||||
json_str = json.dumps(request.form.to_dict(),ensure_ascii=False)
|
||||
log_entry.request_params =json_str
|
||||
elif request.data:
|
||||
json_str = json.dumps({"raw_data": request.data.decode('utf-8')[:500]},ensure_ascii=False)
|
||||
# 原始数据截取前500字符
|
||||
log_entry.request_params =json_str
|
||||
|
||||
|
||||
try:
|
||||
response = func(*args, **kwargs) # 执行原函数(触发 JWT 验证)
|
||||
log_entry.user_id = current_user.id # 此时 current_user 已就绪
|
||||
log_entry.username = current_user.username
|
||||
log_entry.response_code = response.status_code
|
||||
return response
|
||||
except Exception as e:
|
||||
log_entry.error_message = str(e)
|
||||
log_entry.operation_result = 0
|
||||
raise
|
||||
finally:
|
||||
db.session.add(log_entry)
|
||||
db.session.commit()
|
||||
return decorated
|
||||
|
||||
|
||||
# def log_operation(func):
|
||||
# @wraps(func)
|
||||
# def decorated(*args, **kwargs):
|
||||
# # 初始化日志对象(示例结构)
|
||||
# log_entry = {
|
||||
# "path": request.path,
|
||||
# "method": request.method,
|
||||
# "ip": request.remote_addr,
|
||||
# "params": None # 待填充
|
||||
# }
|
||||
|
||||
# try:
|
||||
# # ---- 核心:记录请求参数 ----
|
||||
# if request.method == "GET":
|
||||
# params = request.args.to_dict()
|
||||
# log_entry["params"] = filter_sensitive_data(params)
|
||||
|
||||
# elif request.method == "POST":
|
||||
# if request.is_json:
|
||||
# json_data = request.get_json(silent=True) or {}
|
||||
# log_entry["params"] = filter_sensitive_data(json_data)
|
||||
# elif request.form:
|
||||
# form_data = request.form.to_dict()
|
||||
# log_entry["params"] = filter_sensitive_data(form_data)
|
||||
# elif request.files:
|
||||
# log_entry["files"] = [f.filename for f in request.files.values()]
|
||||
|
||||
# # ---- 执行原函数 ----
|
||||
# start_time = time()
|
||||
# response = func(*args, **kwargs)
|
||||
# duration = time() - start_time
|
||||
|
||||
# # ---- 记录响应 ----
|
||||
# log_entry["status"] = response.status_code
|
||||
# log_entry["duration"] = f"{duration:.3f}s"
|
||||
# return response
|
||||
|
||||
# except Exception as e:
|
||||
# log_entry["error"] = str(e)
|
||||
# raise
|
||||
# finally:
|
||||
# # 实际存储到数据库/文件(此处打印示例)
|
||||
# current_app.logger.info(json.dumps(log_entry, ensure_ascii=False))
|
||||
|
||||
# return decorated
|
||||
|
||||
# JWT 用户回调
|
||||
@jwt.user_lookup_loader
|
||||
def load_user(jwt_header, jwt_data):
|
||||
return User.query.get(jwt_data["sub"])
|
||||
38
app/utils/password_hasher.py
Normal file
38
app/utils/password_hasher.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import hashlib
|
||||
|
||||
def hash_password(password: str) -> str:
|
||||
"""
|
||||
使用 MD5 对密码进行加密(不推荐用于生产环境)
|
||||
|
||||
参数:
|
||||
password (str): 明文密码
|
||||
|
||||
返回:
|
||||
str: MD5 加密后的密码(32 位十六进制字符串)
|
||||
"""
|
||||
# 创建 MD5 哈希对象
|
||||
md5 = hashlib.md5()
|
||||
|
||||
# 更新哈希对象内容(需将字符串编码为 bytes)
|
||||
md5.update(password.encode('utf-8'))
|
||||
|
||||
# 获取十六进制表示的哈希值
|
||||
return md5.hexdigest()
|
||||
|
||||
|
||||
def check_password(plain_password: str, hashed_password: str) -> bool:
|
||||
"""
|
||||
验证密码是否匹配(MD5 版本)
|
||||
|
||||
参数:
|
||||
plain_password (str): 明文密码
|
||||
hashed_password (str): MD5 加密后的密码
|
||||
|
||||
返回:
|
||||
bool: 密码是否匹配
|
||||
"""
|
||||
# 计算明文密码的 MD5 值
|
||||
plain_password_md5 = hash_password(plain_password)
|
||||
|
||||
# 比较两个 MD5 值是否相同
|
||||
return plain_password_md5 == hashed_password
|
||||
0
app/utils/remote_db.py
Normal file
0
app/utils/remote_db.py
Normal file
18
app/utils/response_dict.py
Normal file
18
app/utils/response_dict.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from flask import jsonify
|
||||
|
||||
|
||||
response={
|
||||
'code':0,
|
||||
'data':{},
|
||||
'error':'',
|
||||
'message':'',
|
||||
|
||||
}
|
||||
|
||||
|
||||
def api_response(code=200, message="success", data=None):
|
||||
return jsonify({
|
||||
"code": code,
|
||||
"message": message,
|
||||
"data": data
|
||||
}), code
|
||||
Reference in New Issue
Block a user