This commit is contained in:
ZhuJW
2026-04-22 13:35:40 +08:00
commit 26a7fdf6c0
40 changed files with 11602 additions and 0 deletions

63
app/utils/bagname.py Normal file
View 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

File diff suppressed because it is too large Load Diff

120
app/utils/fst_tree.py Normal file
View 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

View 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
View 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"])

View 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
View File

View 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