jarvis-ai-assistant 0.2.4__py3-none-any.whl → 0.2.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +13 -7
- jarvis/jarvis_agent/edit_file_handler.py +1 -2
- jarvis/jarvis_code_agent/code_agent.py +7 -13
- jarvis/jarvis_data/config_schema.json +2 -19
- jarvis/jarvis_stats/cli.py +72 -5
- jarvis/jarvis_stats/stats.py +175 -70
- jarvis/jarvis_stats/storage.py +53 -1
- jarvis/jarvis_stats/visualizer.py +63 -224
- jarvis/jarvis_tools/cli/main.py +7 -9
- jarvis/jarvis_tools/registry.py +2 -5
- jarvis/jarvis_utils/config.py +6 -8
- jarvis/jarvis_utils/methodology.py +74 -67
- jarvis/jarvis_utils/utils.py +342 -119
- {jarvis_ai_assistant-0.2.4.dist-info → jarvis_ai_assistant-0.2.5.dist-info}/METADATA +11 -2
- {jarvis_ai_assistant-0.2.4.dist-info → jarvis_ai_assistant-0.2.5.dist-info}/RECORD +20 -20
- {jarvis_ai_assistant-0.2.4.dist-info → jarvis_ai_assistant-0.2.5.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.2.4.dist-info → jarvis_ai_assistant-0.2.5.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.2.4.dist-info → jarvis_ai_assistant-0.2.5.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.2.4.dist-info → jarvis_ai_assistant-0.2.5.dist-info}/top_level.txt +0 -0
jarvis/jarvis_stats/stats.py
CHANGED
@@ -14,18 +14,37 @@ from jarvis.jarvis_stats.visualizer import StatsVisualizer
|
|
14
14
|
class StatsManager:
|
15
15
|
"""统计管理器"""
|
16
16
|
|
17
|
+
# 类级别的存储和可视化器实例
|
18
|
+
_storage: Optional[StatsStorage] = None
|
19
|
+
_visualizer: Optional[StatsVisualizer] = None
|
20
|
+
|
21
|
+
@classmethod
|
22
|
+
def _get_storage(cls) -> StatsStorage:
|
23
|
+
"""获取存储实例"""
|
24
|
+
if cls._storage is None:
|
25
|
+
cls._storage = StatsStorage()
|
26
|
+
return cls._storage
|
27
|
+
|
28
|
+
@classmethod
|
29
|
+
def _get_visualizer(cls) -> StatsVisualizer:
|
30
|
+
"""获取可视化器实例"""
|
31
|
+
if cls._visualizer is None:
|
32
|
+
cls._visualizer = StatsVisualizer()
|
33
|
+
return cls._visualizer
|
34
|
+
|
17
35
|
def __init__(self, storage_dir: Optional[str] = None):
|
18
36
|
"""
|
19
|
-
|
37
|
+
初始化统计管理器(保留以兼容旧代码)
|
20
38
|
|
21
39
|
Args:
|
22
40
|
storage_dir: 存储目录路径
|
23
41
|
"""
|
24
|
-
|
25
|
-
|
42
|
+
# 如果提供了特定的存储目录,则重新初始化存储
|
43
|
+
if storage_dir is not None:
|
44
|
+
StatsManager._storage = StatsStorage(storage_dir)
|
26
45
|
|
46
|
+
@staticmethod
|
27
47
|
def increment(
|
28
|
-
self,
|
29
48
|
metric_name: str,
|
30
49
|
amount: Union[int, float] = 1,
|
31
50
|
tags: Optional[Dict[str, str]] = None,
|
@@ -43,11 +62,10 @@ class StatsManager:
|
|
43
62
|
unit: 计量单位,默认为 "count"
|
44
63
|
|
45
64
|
Examples:
|
46
|
-
>>>
|
47
|
-
>>>
|
48
|
-
>>>
|
49
|
-
>>>
|
50
|
-
>>> stats.increment("execute_script", 1, group="tool")
|
65
|
+
>>> StatsManager.increment("page_views")
|
66
|
+
>>> StatsManager.increment("downloads", 5)
|
67
|
+
>>> StatsManager.increment("response_time", 0.123, unit="seconds")
|
68
|
+
>>> StatsManager.increment("execute_script", 1, group="tool")
|
51
69
|
"""
|
52
70
|
# 如果指定了分组,自动添加到 tags 中
|
53
71
|
if group:
|
@@ -55,7 +73,8 @@ class StatsManager:
|
|
55
73
|
tags = {}
|
56
74
|
tags["group"] = group
|
57
75
|
|
58
|
-
|
76
|
+
storage = StatsManager._get_storage()
|
77
|
+
storage.add_metric(
|
59
78
|
metric_name=metric_name,
|
60
79
|
value=float(amount),
|
61
80
|
unit=unit,
|
@@ -63,17 +82,19 @@ class StatsManager:
|
|
63
82
|
tags=tags,
|
64
83
|
)
|
65
84
|
|
66
|
-
|
85
|
+
@staticmethod
|
86
|
+
def list_metrics() -> List[str]:
|
67
87
|
"""
|
68
88
|
列出所有指标
|
69
89
|
|
70
90
|
Returns:
|
71
91
|
指标名称列表
|
72
92
|
"""
|
73
|
-
|
93
|
+
storage = StatsManager._get_storage()
|
94
|
+
return storage.list_metrics()
|
74
95
|
|
96
|
+
@staticmethod
|
75
97
|
def show(
|
76
|
-
self,
|
77
98
|
metric_name: Optional[str] = None,
|
78
99
|
last_hours: Optional[int] = None,
|
79
100
|
last_days: Optional[int] = None,
|
@@ -97,10 +118,9 @@ class StatsManager:
|
|
97
118
|
tags: 过滤标签
|
98
119
|
|
99
120
|
Examples:
|
100
|
-
>>>
|
101
|
-
>>>
|
102
|
-
>>>
|
103
|
-
>>> stats.show("response_time", last_days=7, format="chart") # 图表显示
|
121
|
+
>>> StatsManager.show() # 显示所有指标摘要
|
122
|
+
>>> StatsManager.show("api_calls", last_hours=24) # 显示最近24小时
|
123
|
+
>>> StatsManager.show("response_time", last_days=7, format="chart") # 图表显示
|
104
124
|
"""
|
105
125
|
# 处理时间范围
|
106
126
|
if end_time is None:
|
@@ -116,18 +136,22 @@ class StatsManager:
|
|
116
136
|
|
117
137
|
if metric_name is None:
|
118
138
|
# 显示所有指标摘要
|
119
|
-
|
139
|
+
StatsManager._show_metrics_summary(start_time, end_time, tags)
|
120
140
|
else:
|
121
141
|
# 根据格式显示数据
|
122
142
|
if format == "chart":
|
123
|
-
|
143
|
+
StatsManager._show_chart(
|
144
|
+
metric_name, start_time, end_time, aggregation, tags
|
145
|
+
)
|
124
146
|
elif format == "summary":
|
125
|
-
|
147
|
+
StatsManager._show_summary(
|
148
|
+
metric_name, start_time, end_time, aggregation, tags
|
149
|
+
)
|
126
150
|
else:
|
127
|
-
|
151
|
+
StatsManager._show_table(metric_name, start_time, end_time, tags)
|
128
152
|
|
153
|
+
@staticmethod
|
129
154
|
def plot(
|
130
|
-
self,
|
131
155
|
metric_name: Optional[str] = None,
|
132
156
|
last_hours: Optional[int] = None,
|
133
157
|
last_days: Optional[int] = None,
|
@@ -153,9 +177,8 @@ class StatsManager:
|
|
153
177
|
height: 图表高度
|
154
178
|
|
155
179
|
Examples:
|
156
|
-
>>>
|
157
|
-
>>>
|
158
|
-
>>> stats.plot(tags={"service": "api"}, last_days=7)
|
180
|
+
>>> StatsManager.plot("response_time", last_hours=24)
|
181
|
+
>>> StatsManager.plot(tags={"service": "api"}, last_days=7)
|
159
182
|
"""
|
160
183
|
# 处理时间范围
|
161
184
|
if end_time is None:
|
@@ -171,17 +194,17 @@ class StatsManager:
|
|
171
194
|
|
172
195
|
# 如果指定了metric_name,显示单个图表
|
173
196
|
if metric_name:
|
174
|
-
|
197
|
+
StatsManager._show_chart(
|
175
198
|
metric_name, start_time, end_time, aggregation, tags, width, height
|
176
199
|
)
|
177
200
|
else:
|
178
201
|
# 如果没有指定metric_name,根据标签过滤获取所有匹配的指标
|
179
|
-
|
202
|
+
StatsManager._show_multiple_charts(
|
180
203
|
start_time, end_time, aggregation, tags, width, height
|
181
204
|
)
|
182
205
|
|
206
|
+
@staticmethod
|
183
207
|
def get_stats(
|
184
|
-
self,
|
185
208
|
metric_name: str,
|
186
209
|
last_hours: Optional[int] = None,
|
187
210
|
last_days: Optional[int] = None,
|
@@ -217,14 +240,15 @@ class StatsManager:
|
|
217
240
|
else:
|
218
241
|
start_time = end_time - timedelta(days=7)
|
219
242
|
|
243
|
+
storage = StatsManager._get_storage()
|
220
244
|
if aggregation:
|
221
245
|
# 返回聚合数据
|
222
|
-
return
|
246
|
+
return storage.aggregate_metrics(
|
223
247
|
metric_name, start_time, end_time, aggregation, tags
|
224
248
|
)
|
225
249
|
else:
|
226
250
|
# 返回原始数据
|
227
|
-
records =
|
251
|
+
records = storage.get_metrics(metric_name, start_time, end_time, tags)
|
228
252
|
return {
|
229
253
|
"metric": metric_name,
|
230
254
|
"records": records,
|
@@ -233,28 +257,56 @@ class StatsManager:
|
|
233
257
|
"end_time": end_time.isoformat(),
|
234
258
|
}
|
235
259
|
|
236
|
-
|
260
|
+
@staticmethod
|
261
|
+
def clean_old_data(days_to_keep: int = 30):
|
237
262
|
"""
|
238
263
|
清理旧数据
|
239
264
|
|
240
265
|
Args:
|
241
266
|
days_to_keep: 保留最近N天的数据
|
242
267
|
"""
|
243
|
-
|
268
|
+
storage = StatsManager._get_storage()
|
269
|
+
storage.delete_old_data(days_to_keep)
|
244
270
|
print(f"已清理 {days_to_keep} 天前的数据")
|
245
271
|
|
246
|
-
|
272
|
+
@staticmethod
|
273
|
+
def remove_metric(metric_name: str) -> bool:
|
274
|
+
"""
|
275
|
+
删除指定的指标及其所有数据
|
276
|
+
|
277
|
+
Args:
|
278
|
+
metric_name: 要删除的指标名称
|
279
|
+
|
280
|
+
Returns:
|
281
|
+
True 如果成功删除,False 如果指标不存在
|
282
|
+
"""
|
283
|
+
storage = StatsManager._get_storage()
|
284
|
+
return storage.delete_metric(metric_name)
|
285
|
+
|
286
|
+
@staticmethod
|
287
|
+
def _show_metrics_summary(
|
288
|
+
start_time: Optional[datetime] = None,
|
289
|
+
end_time: Optional[datetime] = None,
|
290
|
+
tags: Optional[Dict[str, str]] = None,
|
291
|
+
):
|
247
292
|
"""显示所有指标摘要"""
|
248
293
|
from rich.console import Console
|
249
294
|
from rich.table import Table
|
250
295
|
|
251
296
|
console = Console()
|
252
|
-
|
297
|
+
storage = StatsManager._get_storage()
|
298
|
+
metrics = storage.list_metrics()
|
253
299
|
|
254
300
|
if not metrics:
|
255
301
|
console.print("[yellow]没有找到任何统计指标[/yellow]")
|
256
302
|
return
|
257
303
|
|
304
|
+
# 如果没有指定时间范围,使用默认值
|
305
|
+
if end_time is None:
|
306
|
+
end_time = datetime.now()
|
307
|
+
if start_time is None:
|
308
|
+
start_time = end_time - timedelta(days=7)
|
309
|
+
|
258
310
|
# 创建表格
|
259
311
|
table = Table(title="统计指标摘要")
|
260
312
|
table.add_column("指标名称", style="cyan")
|
@@ -262,12 +314,20 @@ class StatsManager:
|
|
262
314
|
table.add_column("最后更新", style="yellow")
|
263
315
|
table.add_column("7天数据点", style="magenta")
|
264
316
|
|
265
|
-
#
|
266
|
-
|
267
|
-
start_time = end_time - timedelta(days=7)
|
268
|
-
|
317
|
+
# 过滤满足标签条件的指标
|
318
|
+
displayed_count = 0
|
269
319
|
for metric in metrics:
|
270
|
-
|
320
|
+
# 获取该指标的记录
|
321
|
+
records = storage.get_metrics(metric, start_time, end_time, tags)
|
322
|
+
|
323
|
+
# 如果指定了标签过滤,但没有匹配的记录,跳过该指标
|
324
|
+
if tags and len(records) == 0:
|
325
|
+
continue
|
326
|
+
|
327
|
+
info = storage.get_metric_info(metric)
|
328
|
+
unit = "-"
|
329
|
+
last_updated = "-"
|
330
|
+
|
271
331
|
if info:
|
272
332
|
unit = info.get("unit", "-")
|
273
333
|
last_updated = info.get("last_updated", "-")
|
@@ -280,31 +340,38 @@ class StatsManager:
|
|
280
340
|
except:
|
281
341
|
pass
|
282
342
|
|
283
|
-
|
284
|
-
|
285
|
-
|
343
|
+
count = len(records)
|
344
|
+
table.add_row(metric, unit, last_updated, str(count))
|
345
|
+
displayed_count += 1
|
286
346
|
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
347
|
+
if displayed_count == 0:
|
348
|
+
console.print("[yellow]没有找到符合条件的指标[/yellow]")
|
349
|
+
if tags:
|
350
|
+
console.print(f"过滤条件: {tags}")
|
351
|
+
else:
|
352
|
+
console.print(table)
|
353
|
+
console.print(f"\n[green]总计: {displayed_count} 个指标[/green]")
|
354
|
+
if tags:
|
355
|
+
console.print(f"过滤条件: {tags}")
|
291
356
|
|
357
|
+
@staticmethod
|
292
358
|
def _show_table(
|
293
|
-
self,
|
294
359
|
metric_name: str,
|
295
360
|
start_time: datetime,
|
296
361
|
end_time: datetime,
|
297
362
|
tags: Optional[Dict[str, str]],
|
298
363
|
):
|
299
364
|
"""以表格形式显示数据"""
|
300
|
-
|
365
|
+
storage = StatsManager._get_storage()
|
366
|
+
visualizer = StatsManager._get_visualizer()
|
367
|
+
records = storage.get_metrics(metric_name, start_time, end_time, tags)
|
301
368
|
|
302
369
|
# 获取指标信息
|
303
|
-
info =
|
370
|
+
info = storage.get_metric_info(metric_name)
|
304
371
|
unit = info.get("unit", "") if info else ""
|
305
372
|
|
306
373
|
# 使用visualizer显示表格
|
307
|
-
|
374
|
+
visualizer.show_table(
|
308
375
|
records=records,
|
309
376
|
metric_name=metric_name,
|
310
377
|
unit=unit,
|
@@ -313,8 +380,8 @@ class StatsManager:
|
|
313
380
|
tags_filter=tags,
|
314
381
|
)
|
315
382
|
|
383
|
+
@staticmethod
|
316
384
|
def _show_chart(
|
317
|
-
self,
|
318
385
|
metric_name: str,
|
319
386
|
start_time: datetime,
|
320
387
|
end_time: datetime,
|
@@ -324,8 +391,11 @@ class StatsManager:
|
|
324
391
|
height: Optional[int] = None,
|
325
392
|
):
|
326
393
|
"""显示图表"""
|
394
|
+
storage = StatsManager._get_storage()
|
395
|
+
visualizer = StatsManager._get_visualizer()
|
396
|
+
|
327
397
|
# 获取聚合数据
|
328
|
-
aggregated =
|
398
|
+
aggregated = storage.aggregate_metrics(
|
329
399
|
metric_name, start_time, end_time, aggregation, tags
|
330
400
|
)
|
331
401
|
|
@@ -334,19 +404,30 @@ class StatsManager:
|
|
334
404
|
return
|
335
405
|
|
336
406
|
# 获取指标信息
|
337
|
-
info =
|
407
|
+
info = storage.get_metric_info(metric_name)
|
338
408
|
unit = info.get("unit", "") if info else ""
|
339
409
|
|
340
410
|
# 准备数据
|
341
|
-
|
411
|
+
first_item = next(iter(aggregated.values()), None)
|
412
|
+
is_simple_count = (
|
413
|
+
first_item
|
414
|
+
and first_item.get("min") == 1
|
415
|
+
and first_item.get("max") == 1
|
416
|
+
and first_item.get("avg") == 1
|
417
|
+
)
|
342
418
|
|
343
|
-
|
419
|
+
if unit == "count" or is_simple_count:
|
420
|
+
# 对于计数类指标,使用总和更有意义
|
421
|
+
data = {k: v["sum"] for k, v in aggregated.items()}
|
422
|
+
else:
|
423
|
+
# 对于其他指标(如耗时),使用平均值
|
424
|
+
data = {k: v["avg"] for k, v in aggregated.items()} # 设置可视化器尺寸
|
344
425
|
if width or height:
|
345
|
-
|
346
|
-
|
426
|
+
visualizer.width = width or visualizer.width
|
427
|
+
visualizer.height = height or visualizer.height
|
347
428
|
|
348
429
|
# 绘制图表
|
349
|
-
chart =
|
430
|
+
chart = visualizer.plot_line_chart(
|
350
431
|
data=data,
|
351
432
|
title=f"{metric_name} - {aggregation}聚合",
|
352
433
|
unit=unit,
|
@@ -356,12 +437,22 @@ class StatsManager:
|
|
356
437
|
print(chart)
|
357
438
|
|
358
439
|
# 显示时间范围
|
359
|
-
|
360
|
-
|
440
|
+
from rich.panel import Panel
|
441
|
+
from rich.console import Console
|
442
|
+
|
443
|
+
console = Console()
|
444
|
+
console.print(
|
445
|
+
Panel(
|
446
|
+
f"[cyan]{start_time.strftime('%Y-%m-%d %H:%M')}[/] ~ [cyan]{end_time.strftime('%Y-%m-%d %H:%M')}[/]",
|
447
|
+
title="[bold]时间范围[/bold]",
|
448
|
+
expand=False,
|
449
|
+
style="dim",
|
450
|
+
border_style="green",
|
451
|
+
)
|
361
452
|
)
|
362
453
|
|
454
|
+
@staticmethod
|
363
455
|
def _show_multiple_charts(
|
364
|
-
self,
|
365
456
|
start_time: datetime,
|
366
457
|
end_time: datetime,
|
367
458
|
aggregation: str,
|
@@ -373,15 +464,16 @@ class StatsManager:
|
|
373
464
|
from rich.console import Console
|
374
465
|
|
375
466
|
console = Console()
|
467
|
+
storage = StatsManager._get_storage()
|
376
468
|
|
377
469
|
# 获取所有指标
|
378
|
-
all_metrics =
|
470
|
+
all_metrics = StatsManager.list_metrics()
|
379
471
|
|
380
472
|
# 根据标签过滤指标
|
381
473
|
matched_metrics = []
|
382
474
|
for metric in all_metrics:
|
383
475
|
# 获取该指标在时间范围内的数据
|
384
|
-
records =
|
476
|
+
records = storage.get_metrics(metric, start_time, end_time, tags)
|
385
477
|
if records: # 如果有匹配标签的数据
|
386
478
|
matched_metrics.append(metric)
|
387
479
|
|
@@ -396,12 +488,12 @@ class StatsManager:
|
|
396
488
|
if i > 0:
|
397
489
|
console.print("\n" + "=" * 80 + "\n") # 分隔符
|
398
490
|
|
399
|
-
|
491
|
+
StatsManager._show_chart(
|
400
492
|
metric, start_time, end_time, aggregation, tags, width, height
|
401
493
|
)
|
402
494
|
|
495
|
+
@staticmethod
|
403
496
|
def _show_summary(
|
404
|
-
self,
|
405
497
|
metric_name: str,
|
406
498
|
start_time: datetime,
|
407
499
|
end_time: datetime,
|
@@ -409,8 +501,11 @@ class StatsManager:
|
|
409
501
|
tags: Optional[Dict[str, str]],
|
410
502
|
):
|
411
503
|
"""显示汇总信息"""
|
504
|
+
storage = StatsManager._get_storage()
|
505
|
+
visualizer = StatsManager._get_visualizer()
|
506
|
+
|
412
507
|
# 获取聚合数据
|
413
|
-
aggregated =
|
508
|
+
aggregated = storage.aggregate_metrics(
|
414
509
|
metric_name, start_time, end_time, aggregation, tags
|
415
510
|
)
|
416
511
|
|
@@ -419,15 +514,25 @@ class StatsManager:
|
|
419
514
|
return
|
420
515
|
|
421
516
|
# 获取指标信息
|
422
|
-
info =
|
517
|
+
info = storage.get_metric_info(metric_name)
|
423
518
|
unit = info.get("unit", "") if info else ""
|
424
519
|
|
425
520
|
# 显示汇总
|
426
|
-
summary =
|
521
|
+
summary = visualizer.show_summary(aggregated, metric_name, unit, tags)
|
427
522
|
if summary: # 如果返回了内容才打印(兼容性)
|
428
523
|
print(summary)
|
429
524
|
|
430
525
|
# 显示时间范围
|
431
|
-
|
432
|
-
|
526
|
+
from rich.panel import Panel
|
527
|
+
from rich.console import Console
|
528
|
+
|
529
|
+
console = Console()
|
530
|
+
console.print(
|
531
|
+
Panel(
|
532
|
+
f"[cyan]{start_time.strftime('%Y-%m-%d %H:%M')}[/] ~ [cyan]{end_time.strftime('%Y-%m-%d %H:%M')}[/]",
|
533
|
+
title="[bold]时间范围[/bold]",
|
534
|
+
expand=False,
|
535
|
+
style="dim",
|
536
|
+
border_style="green",
|
537
|
+
)
|
433
538
|
)
|
jarvis/jarvis_stats/storage.py
CHANGED
@@ -240,8 +240,23 @@ class StatsStorage:
|
|
240
240
|
|
241
241
|
def list_metrics(self) -> List[str]:
|
242
242
|
"""列出所有指标"""
|
243
|
+
# 从元数据文件获取指标
|
243
244
|
meta = self._load_json(self.meta_file)
|
244
|
-
|
245
|
+
metrics_from_meta = set(meta.get("metrics", {}).keys())
|
246
|
+
|
247
|
+
# 扫描所有数据文件获取实际存在的指标
|
248
|
+
metrics_from_data = set()
|
249
|
+
for data_file in self.data_dir.glob("stats_*.json"):
|
250
|
+
try:
|
251
|
+
data = self._load_json(data_file)
|
252
|
+
metrics_from_data.update(data.keys())
|
253
|
+
except (json.JSONDecodeError, OSError):
|
254
|
+
# 忽略无法读取的文件
|
255
|
+
continue
|
256
|
+
|
257
|
+
# 合并两个来源的指标并返回排序后的列表
|
258
|
+
all_metrics = metrics_from_meta.union(metrics_from_data)
|
259
|
+
return sorted(list(all_metrics))
|
245
260
|
|
246
261
|
def aggregate_metrics(
|
247
262
|
self,
|
@@ -310,6 +325,43 @@ class StatsStorage:
|
|
310
325
|
|
311
326
|
return result
|
312
327
|
|
328
|
+
def delete_metric(self, metric_name: str) -> bool:
|
329
|
+
"""
|
330
|
+
删除指定的指标及其所有数据
|
331
|
+
|
332
|
+
Args:
|
333
|
+
metric_name: 要删除的指标名称
|
334
|
+
|
335
|
+
Returns:
|
336
|
+
True 如果成功删除,False 如果指标不存在
|
337
|
+
"""
|
338
|
+
# 检查指标是否存在
|
339
|
+
meta = self._load_json(self.meta_file)
|
340
|
+
if metric_name not in meta.get("metrics", {}):
|
341
|
+
return False
|
342
|
+
|
343
|
+
# 从元数据中删除指标
|
344
|
+
del meta["metrics"][metric_name]
|
345
|
+
self._save_json(self.meta_file, meta)
|
346
|
+
|
347
|
+
# 遍历所有数据文件,删除该指标的数据
|
348
|
+
for data_file in self.data_dir.glob("stats_*.json"):
|
349
|
+
try:
|
350
|
+
data = self._load_json(data_file)
|
351
|
+
if metric_name in data:
|
352
|
+
del data[metric_name]
|
353
|
+
# 如果文件中还有其他数据,保存更新后的文件
|
354
|
+
if data:
|
355
|
+
self._save_json(data_file, data)
|
356
|
+
# 如果文件变空了,删除文件
|
357
|
+
else:
|
358
|
+
data_file.unlink()
|
359
|
+
except Exception:
|
360
|
+
# 忽略单个文件的错误,继续处理其他文件
|
361
|
+
pass
|
362
|
+
|
363
|
+
return True
|
364
|
+
|
313
365
|
def delete_old_data(self, days_to_keep: int = 30):
|
314
366
|
"""删除旧数据"""
|
315
367
|
cutoff_date = (datetime.now() - timedelta(days=days_to_keep)).date()
|