Files

428 lines
17 KiB
Python
Raw Permalink Normal View History

2026-07-10 18:55:55 +08:00
from collections import defaultdict
from datetime import datetime, timedelta
import json
def process_incident_data_gen6(test_val):
"""
处理事故数据,按照incident_name去重(移除原incident_time组合去重),移除idc_tickcount_ms排序,其他逻辑不变。
Args:
test_val (list): 原始数据列表
Returns:
dict: 格式化后的数据
"""
# 1. 仅按照 incident_name 去重(核心修改点1:移除incident_time组合)
unique_data_dict = {}
for item in test_val:
name = item.get("incident_name")
# 仅判断incident_name是否存在,不再依赖incident_time
if name:
# 仅用incident_name作为去重键
key = name
if key not in unique_data_dict:
unique_data_dict[key] = item
# 获取去重后的数据列表(核心修改点2:移除排序步骤)
unique_data_list = list(unique_data_dict.values())
# 3. 提取指定字段(原逻辑完全保留,仅将遍历对象从sorted_data改为unique_data_list
processed_events_data = []
for index, item in enumerate(unique_data_list, 1):
incident_time_str = item.get("incident_time")
if not incident_time_str:
continue
try:
# 解析新的时间格式 "2025-11-30 20:51:35.684000"
# 先尝试直接解析完整格式
try:
# 移除毫秒部分,只保留到秒
base_time_str = incident_time_str.split('.')[0]
incident_datetime = datetime.strptime(base_time_str, "%Y-%m-%d %H:%M:%S")
except ValueError:
# 如果失败,尝试其他可能的格式
try:
incident_datetime = datetime.fromisoformat(incident_time_str.replace('Z', '+00:00'))
except:
# 再次失败,使用当前时间
incident_datetime = datetime.now()
# 格式化日期和时间
date_str_result = incident_datetime.strftime("%Y-%m-%d")
time_str_result = incident_datetime.strftime("%H:%M:%S")
except (ValueError, TypeError) as e:
print(f"Warning: Failed to parse incident_time '{incident_time_str}': {e}")
# 使用默认值
date_str_result = item.get("dt", "").split(' ')[0] if item.get("dt") else datetime.now().strftime("%Y-%m-%d")
time_str_result = "00:00:00"
# 创建新的字典
processed_item = {
"id": index,
"oneid": item.get("oneid", ""),
"date": date_str_result,
"time": time_str_result,
"incident_name": item.get("incident_name", ""),
"incident_description": "",
}
processed_events_data.append(processed_item)
# 4. 构建最终返回的字典
total_count = len(processed_events_data)
result = {"data": processed_events_data, "total": total_count}
return result
def aggregate_and_sort_incidents_gen6(raw_data):
"""
简化版本:按(incident_time + incident_name)精确聚合
适用于同一事件的incident_time完全一致的情况
"""
aggregate_dict = {}
for item in raw_data:
incident_time = item.get("incident_time", "1970-01-01 00:00:00.000")
incident_name = item.get("incident_name", "unknown_incident")
aggregate_key = (incident_time, incident_name)
if aggregate_key not in aggregate_dict:
aggregate_dict[aggregate_key] = {
"base_info": {
"oneid": item.get("oneid"),
"incident_time": incident_time,
"speed": item.get("speed"),
"gps_heading": item.get("gps_heading"),
"incident_name": incident_name,
"latitude": item.get("latitude"),
"longitude": item.get("longitude"),
"odometer": item.get("odometer"),
"sw_version": item.get("sw_version"),
"carmodel": item.get("carmodel"),
"session_id": item.get("session_id"),
"dt": item.get("dt")
},
"signal_data": {}
}
signal_key = item.get("key", "unknown_key")
aggregate_dict[aggregate_key]["signal_data"][signal_key] = {
'signal_value': item.get('signal_value', '0.0'),
"odometer": item.get('odometer', '0.0')
}
# 按incident_time排序
sorted_result = sorted(
aggregate_dict.values(),
key=lambda x: x["base_info"]["incident_time"]
)
return sorted_result
def aggregate_line_list_gen6(map_list):
"""
按incident_time聚合事件数据,生成时间线列表
改进点:
1. 使用正确的时间字段名
2. 支持精确的时间排序
3. 处理同一时间点的多条记录
4. 添加健壮的错误处理
"""
aggregated_data = {}
# 遍历所有事件
for item in map_list:
try:
base_info = item['base_info']
# 提取需要的字段
incident_time_str = base_info.get('incident_time', '1970-01-01 00:00:00.000')
speed = base_info.get('speed', '0')
oneid = base_info.get('oneid', '')
latitude = base_info.get('latitude', '0')
longitude = base_info.get('longitude', '0')
incident_name = base_info.get('incident_name', 'Unknown Incident')
# 创建聚合键(使用完整时间字符串)
aggregate_key = incident_time_str
# 聚合处理
if aggregate_key not in aggregated_data:
# 新的时间点,初始化记录
aggregated_data[aggregate_key] = {
'incident_time': incident_time_str, # 使用正确的字段名
'speed': speed,
'oneid': oneid,
'latitude': latitude,
'longitude': longitude,
'incident_names': [incident_name], # 使用复数形式,表示可能有多个
'record_count': 1 # 记录该时间点的记录数
}
else:
# 已存在的时间点
existing = aggregated_data[aggregate_key]
# 可选:处理同一时间点不同记录的字段冲突
# 这里选择保留第一条的速度,但如果需要可以取平均值或最新值
# existing['speed'] = str((float(existing['speed']) + float(speed)) / 2)
# 添加事件名称(去重)
if incident_name not in existing['incident_names']:
existing['incident_names'].append(incident_name)
existing['record_count'] += 1
except (KeyError, TypeError) as e:
print(f"Warning: Skipping item due to missing field: {e}")
continue
# 转换为列表形式,并按时间正序排序
def get_sort_time(record):
try:
time_str = record['incident_time']
if '.' in time_str:
# 处理带毫秒的时间
return datetime.strptime(time_str, "%Y-%m-%d %H:%M:%S.%f")
else:
# 处理不带毫秒的时间
return datetime.strptime(time_str, "%Y-%m-%d %H:%M:%S")
except (ValueError, TypeError) as e:
print(f"Warning: Failed to parse time '{record['incident_time']}' for sorting: {e}")
return datetime(1970, 1, 1) # 返回默认时间
# 按incident_time正序排序(从小到大)
result = sorted(aggregated_data.values(), key=get_sort_time)
return result
def get_logging_list_gen6(map_list):
"""
处理map_list数据,生成logging列表,并按incident_time正序排序
同时对每个记录的signals按name字段进行字符顺序排序
"""
# 1. 首先对map_list按incident_time进行排序
def get_sort_key(item):
try:
incident_time_str = item['base_info']['incident_time']
# 处理时间字符串,统一格式
if '.' in incident_time_str:
# 保留到毫秒级别进行排序
base_time_str = incident_time_str.split('.')[0]
milliseconds = incident_time_str.split('.')[1][:6] # 取最多6位毫秒
full_time_str = f"{base_time_str}.{milliseconds}"
return datetime.strptime(full_time_str, "%Y-%m-%d %H:%M:%S.%f")
else:
return datetime.strptime(incident_time_str, "%Y-%m-%d %H:%M:%S")
except (ValueError, TypeError, KeyError) as e:
print(f"Warning: Failed to parse incident_time for sorting: {e}")
# 解析失败时返回一个很早的时间,确保这些记录排在最后
return datetime(1970, 1, 1)
# 按incident_time正序排序(从小到大)
sorted_map_list = sorted(map_list, key=get_sort_key)
# 2. 处理排序后的数据
result = []
id = 0
for item in sorted_map_list:
id += 1
row_data = {}
row_data['id'] = id
base_info = item['base_info']
# 处理时间格式 - 新格式为 "2025-11-30 20:23:05.394000"
incident_time_str = base_info['incident_time']
try:
# 尝试解析新格式的时间
if '.' in incident_time_str:
# 移除毫秒部分,只保留到秒
base_time_str = incident_time_str.split('.')[0]
dt = datetime.strptime(base_time_str, "%Y-%m-%d %H:%M:%S")
else:
dt = datetime.strptime(incident_time_str, "%Y-%m-%d %H:%M:%S")
row_data['time'] = dt.strftime("%H:%M:%S")
except (ValueError, TypeError) as e:
print(f"Warning: Failed to parse incident_time '{incident_time_str}': {e}")
# 使用默认时间
row_data['time'] = "00:00:00"
# 处理经纬度 - 可能为 "NULL" 字符串
longitude = str(base_info.get('longitude', '')).strip()
latitude = str(base_info.get('latitude', '')).strip()
# 处理 "NULL" 字符串
if longitude.lower() == "null" or longitude == "":
longitude = "N/A"
if latitude.lower() == "null" or latitude == "":
latitude = "N/A"
# 格式化坐标显示
row_data['coordinates'] = f"{longitude[:12]}\n{latitude[:12]}"
# 处理idc_tickcount_ms
row_data['idc_tickcount_ms'] = base_info.get('idc_tickcount_ms_int', 0)
# 处理速度 - 可能为 "NULL" 字符串
speed = str(base_info.get('speed', '')).strip()
if speed.lower() == "null" or speed == "":
speed = "0"
row_data['speed'] = speed
# 处理incident信息 - 新格式没有Incident__description
row_data['incident'] = {
"name": base_info.get('incident_name', 'Unknown Incident'),
"description": "" # 使用incident_name作为描述
}
# 处理信号数据
row_data['signals'] = []
for key, value in item["signal_data"].items():
signals_name_value = {}
signals_name_value["name"] = key
# 新格式使用signal_value字段
signal_value = value.get('signal_value', '')
if signal_value is None or signal_value == "":
signal_value = "N/A"
signals_name_value["value"] = signal_value
# 匹配到meanning的值
meaning_result=match_meaning(key,signal_value)
# 新格式没有Meaning字段,使用空字符串
signals_name_value["meaning"] = meaning_result
row_data['signals'].append(signals_name_value)
# === 新增:对signals列表按name字段进行字符顺序排序 ===
row_data['signals'] = sorted(row_data['signals'], key=lambda x: x['name'])
result.append(row_data)
return result
def convert_to_histro_data(raw_data):
"""
转换原始数据为前端折线图格式(time保留%Y-%m-%d %H:%M:%S.%f格式)
:param raw_data: 原始字典列表
:return: 前端所需格式的列表
"""
# 步骤1:按signal分组
signal_groups = defaultdict(list)
for item in raw_data:
signal_groups[item["signal"]].append(item)
# 步骤2:处理每个分组,构造最终数据
chart_data = []
for signal_name, items in signal_groups.items():
# 修复1:统一使用start_ts字段进行排序(因为这是时间轴数据)
def sort_by_start_ts(item):
return datetime.strptime(item["time"], "%Y-%m-%d %H:%M:%S.%f")
sorted_items = sorted(items, key=sort_by_start_ts)
# 修复3:统一使用time作为X轴时间数据(与排序字段一致)
time_list = [item["time"] for item in sorted_items] # 使用start_ts作为时间轴
values_list = [float(item["value"]) for item in sorted_items] # value转数值
# 子步骤3:构造当前signal的折线图数据
chart_item = {
"signalName": signal_name,
"time": time_list, # X轴时间数据
"values": values_list # Y轴数值数据
}
chart_data.append(chart_item)
return chart_data
def convert_to_histro_data_v2(raw_data):
"""
转换原始数据为前端图表格式 {x: 时间, y: 数值, value: 数值}
:param raw_data: 原始字典列表,需包含 "signal"、"time"、"value" 字段
:return: 字典(key=signal名称,value=对应{x,y,value}格式的列表);
若需合并所有signal为单列表,可取消注释对应逻辑
"""
# 步骤1:按signal字段分组
signal_groups = defaultdict(list)
for item in raw_data:
signal_groups[item["signal"]].append(item)
# 步骤2:处理每个分组,构造目标格式数据
chart_data = {}
for signal_name, items in signal_groups.items():
# 按time字段排序(保证时间轴顺序)
def sort_by_time(item):
return datetime.strptime(item["time"], "%Y-%m-%d %H:%M:%S.%f")
sorted_items = sorted(items, key=sort_by_time)
# 构造 {x: 时间, y: 数值, value: 数值} 格式的列表
signal_item_list = []
for item in sorted_items:
val = float(item["value"]) # 确保数值类型为浮点数
signal_item_list.append({
"x": item["time"], # x轴:时间字符串(对应示例中的"销量4"类标签)
"y": item['valueExplanation'], # y轴:原始value数值
"value": val # value字段:与y轴数值一致(匹配示例格式)
})
# ========== 新增逻辑开始 ==========
# 获取排序后第一条原始数据
first_raw_item = sorted_items[0]
# 判断is_pre_value_needed是否非空(处理空字符串、None、"NULL"等情况)
is_needed = first_raw_item.get("is_pre_value_needed", "")
if is_needed and is_needed.strip() and is_needed != "NULL":
try:
# 提取pre相关值并转换类型
pre_val = float(first_raw_item["pre_value"])
pre_explanation = first_raw_item["pre_valueExplanation"]
first_time = first_raw_item["time"] # 在第一条数据的时间基础上减10秒
first_time_dt = datetime.strptime(first_time, "%Y-%m-%d %H:%M:%S.%f")
pre_time_dt = first_time_dt - timedelta(seconds=10)
pre_time_str = pre_time_dt.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
# 构造前置数据项
pre_item = {
"x": '', # x轴:第一条数据的时间
"y": pre_explanation, # y轴:pre_value对应的说明
"value": pre_val, # value字段:pre_value的数值
"show":1
}
# 插入到列表最前面
signal_item_list.insert(0, pre_item)
except KeyError as e:
print(f"警告:第一条数据缺少{e}字段,跳过前置数据插入")
except ValueError as e:
print(f"警告:pre_value转换浮点数失败({e}),跳过前置数据插入")
# ========== 新增逻辑结束 ==========
# 按signal名称存储结果
chart_data[signal_name] = signal_item_list
return chart_data
def match_meaning(singla_name,value):
import os
current_dir = os.path.dirname(os.path.abspath(__file__))
json_path = os.path.join(current_dir, "singal_value.json")
with open(json_path, "r", encoding="utf-8") as f:
mean_data = json.load(f)
singla=singla_name.split('.')[-1]
meaning_state=mean_data.get(singla,None)
if meaning_state:
return meaning_state[value] if meaning_state[value] else ''
else:
return ''