2026-04-02 18:46:42 +08:00
|
|
|
|
"""种子数据 - 天普园区设备信息、用户、告警规则、碳排放因子、报表模板、历史告警"""
|
2026-04-01 13:36:06 +08:00
|
|
|
|
import asyncio
|
2026-04-02 18:46:42 +08:00
|
|
|
|
import json
|
2026-04-01 13:36:06 +08:00
|
|
|
|
import sys
|
2026-04-02 18:46:42 +08:00
|
|
|
|
import os
|
|
|
|
|
|
from datetime import datetime, timezone, timedelta
|
2026-04-01 13:36:06 +08:00
|
|
|
|
|
2026-04-02 18:46:42 +08:00
|
|
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "backend"))
|
|
|
|
|
|
|
|
|
|
|
|
from sqlalchemy import select
|
|
|
|
|
|
from app.core.database import async_session, engine
|
2026-04-01 13:36:06 +08:00
|
|
|
|
from app.core.security import hash_password
|
|
|
|
|
|
from app.models.user import User, Role
|
|
|
|
|
|
from app.models.device import Device, DeviceType, DeviceGroup
|
2026-04-02 18:46:42 +08:00
|
|
|
|
from app.models.alarm import AlarmRule, AlarmEvent
|
2026-04-01 13:36:06 +08:00
|
|
|
|
from app.models.carbon import EmissionFactor
|
feat: add system settings, audit log, device detail, dark mode, i18n, email notifications
System Management:
- System Settings page with 8 configurable parameters (admin only)
- Audit Log page with filterable table (user, action, resource, date range)
- Audit logging wired into auth, devices, users, alarms, reports API handlers
- SystemSetting model + migration (002)
Device Detail:
- Dedicated /devices/:id page with 4 tabs (realtime, historical trends, alarm history, device info)
- ECharts historical charts with granularity/time range selectors
- Device name clickable in Devices and Monitoring tables → navigates to detail
Email & Scheduling:
- Email service with SMTP support (STARTTLS/SSL/plain)
- Alarm email notification with professional HTML template
- Report scheduler using APScheduler for cron-based auto-generation
- Scheduled report task seeded (daily at 8am)
UI Enhancements:
- Dark mode toggle (persisted to localStorage, Ant Design darkAlgorithm)
- Data comparison view in Analysis page (dual date range, side-by-side metrics)
- i18n framework (i18next) with zh/en translations for menu and common UI
- Language switcher in header (中文/English)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 19:42:22 +08:00
|
|
|
|
from app.models.report import ReportTemplate, ReportTask
|
2026-04-01 13:36:06 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def seed():
|
|
|
|
|
|
async with async_session() as session:
|
2026-04-02 18:46:42 +08:00
|
|
|
|
# =====================================================================
|
|
|
|
|
|
# 1. 角色 (6 roles with JSON permissions)
|
|
|
|
|
|
# =====================================================================
|
|
|
|
|
|
roles_data = [
|
|
|
|
|
|
{
|
|
|
|
|
|
"name": "admin",
|
|
|
|
|
|
"display_name": "园区管理员",
|
|
|
|
|
|
"description": "平台最高管理者,负责全局配置和用户管理",
|
|
|
|
|
|
"permissions": json.dumps([
|
|
|
|
|
|
"user:read", "user:write", "user:delete",
|
|
|
|
|
|
"device:read", "device:write", "device:delete",
|
|
|
|
|
|
"energy:read", "energy:export",
|
|
|
|
|
|
"alarm:read", "alarm:write", "alarm:acknowledge",
|
|
|
|
|
|
"report:read", "report:write", "report:export",
|
|
|
|
|
|
"carbon:read", "carbon:write",
|
|
|
|
|
|
"system:config", "system:audit",
|
|
|
|
|
|
]),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"name": "energy_manager",
|
|
|
|
|
|
"display_name": "能源主管",
|
|
|
|
|
|
"description": "负责园区能源运行管理和优化决策",
|
|
|
|
|
|
"permissions": json.dumps([
|
|
|
|
|
|
"device:read", "device:write",
|
|
|
|
|
|
"energy:read", "energy:export",
|
|
|
|
|
|
"alarm:read", "alarm:write", "alarm:acknowledge",
|
|
|
|
|
|
"report:read", "report:write", "report:export",
|
|
|
|
|
|
"carbon:read", "carbon:write",
|
|
|
|
|
|
]),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"name": "area_manager",
|
|
|
|
|
|
"display_name": "区域负责人",
|
|
|
|
|
|
"description": "负责特定区域或建筑的能源管理",
|
|
|
|
|
|
"permissions": json.dumps([
|
|
|
|
|
|
"device:read",
|
|
|
|
|
|
"energy:read", "energy:export",
|
|
|
|
|
|
"alarm:read", "alarm:acknowledge",
|
|
|
|
|
|
"report:read", "report:export",
|
|
|
|
|
|
"carbon:read",
|
|
|
|
|
|
]),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"name": "operator",
|
|
|
|
|
|
"display_name": "设备运维员",
|
|
|
|
|
|
"description": "负责设备日常运维和故障处理",
|
|
|
|
|
|
"permissions": json.dumps([
|
|
|
|
|
|
"device:read", "device:write",
|
|
|
|
|
|
"energy:read",
|
|
|
|
|
|
"alarm:read", "alarm:acknowledge",
|
|
|
|
|
|
"report:read",
|
|
|
|
|
|
]),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"name": "analyst",
|
|
|
|
|
|
"display_name": "财务分析员",
|
|
|
|
|
|
"description": "负责能源成本分析和财务报表",
|
|
|
|
|
|
"permissions": json.dumps([
|
|
|
|
|
|
"energy:read", "energy:export",
|
|
|
|
|
|
"report:read", "report:write", "report:export",
|
|
|
|
|
|
"carbon:read",
|
|
|
|
|
|
]),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"name": "visitor",
|
|
|
|
|
|
"display_name": "普通访客",
|
|
|
|
|
|
"description": "仅查看公开信息",
|
|
|
|
|
|
"permissions": json.dumps([
|
|
|
|
|
|
"energy:read",
|
|
|
|
|
|
"device:read",
|
|
|
|
|
|
]),
|
|
|
|
|
|
},
|
2026-04-01 13:36:06 +08:00
|
|
|
|
]
|
2026-04-02 18:46:42 +08:00
|
|
|
|
roles = [Role(**r) for r in roles_data]
|
2026-04-01 13:36:06 +08:00
|
|
|
|
session.add_all(roles)
|
|
|
|
|
|
|
2026-04-02 18:46:42 +08:00
|
|
|
|
# =====================================================================
|
|
|
|
|
|
# 2. 用户 (admin + 2 demo users)
|
|
|
|
|
|
# =====================================================================
|
2026-04-01 13:36:06 +08:00
|
|
|
|
users = [
|
2026-04-02 18:46:42 +08:00
|
|
|
|
User(username="admin", hashed_password=hash_password("admin123"),
|
|
|
|
|
|
full_name="系统管理员", role="admin", email="admin@tianpu.com",
|
|
|
|
|
|
phone="13800000001"),
|
|
|
|
|
|
User(username="energy_mgr", hashed_password=hash_password("tianpu123"),
|
|
|
|
|
|
full_name="张能源", role="energy_manager", email="energy@tianpu.com",
|
|
|
|
|
|
phone="13800000002"),
|
|
|
|
|
|
User(username="operator1", hashed_password=hash_password("tianpu123"),
|
|
|
|
|
|
full_name="李运维", role="operator", email="op1@tianpu.com",
|
|
|
|
|
|
phone="13800000003"),
|
2026-04-02 21:14:42 +08:00
|
|
|
|
User(username="visitor", hashed_password=hash_password("visitor123"),
|
|
|
|
|
|
full_name="访客", role="visitor", email=None,
|
|
|
|
|
|
phone=None),
|
2026-04-01 13:36:06 +08:00
|
|
|
|
]
|
|
|
|
|
|
session.add_all(users)
|
|
|
|
|
|
|
2026-04-02 18:46:42 +08:00
|
|
|
|
# =====================================================================
|
|
|
|
|
|
# 3. 设备类型 (8 types)
|
|
|
|
|
|
# =====================================================================
|
2026-04-01 13:36:06 +08:00
|
|
|
|
device_types = [
|
|
|
|
|
|
DeviceType(code="pv_inverter", name="光伏逆变器", icon="solar-panel",
|
2026-04-02 18:46:42 +08:00
|
|
|
|
data_fields=["power", "daily_energy", "total_energy",
|
|
|
|
|
|
"dc_voltage", "ac_voltage", "temperature", "fault_code"]),
|
2026-04-01 13:36:06 +08:00
|
|
|
|
DeviceType(code="heat_pump", name="空气源热泵", icon="heat-pump",
|
2026-04-02 18:46:42 +08:00
|
|
|
|
data_fields=["power", "cop", "inlet_temp", "outlet_temp",
|
|
|
|
|
|
"flow_rate", "outdoor_temp", "mode"]),
|
2026-04-01 13:36:06 +08:00
|
|
|
|
DeviceType(code="solar_thermal", name="光热集热器", icon="solar-thermal",
|
|
|
|
|
|
data_fields=["heat_output", "collector_temp", "irradiance", "pump_status"]),
|
|
|
|
|
|
DeviceType(code="battery", name="储能系统", icon="battery",
|
2026-04-02 18:46:42 +08:00
|
|
|
|
data_fields=["power", "soc", "voltage", "current",
|
|
|
|
|
|
"temperature", "cycle_count"]),
|
2026-04-01 13:36:06 +08:00
|
|
|
|
DeviceType(code="meter", name="智能电表", icon="meter",
|
2026-04-02 18:46:42 +08:00
|
|
|
|
data_fields=["power", "energy", "voltage", "current",
|
|
|
|
|
|
"power_factor", "frequency"]),
|
2026-04-01 13:36:06 +08:00
|
|
|
|
DeviceType(code="sensor", name="温湿度传感器", icon="sensor",
|
|
|
|
|
|
data_fields=["temperature", "humidity"]),
|
|
|
|
|
|
DeviceType(code="heat_meter", name="热量表", icon="heat-meter",
|
2026-04-02 18:46:42 +08:00
|
|
|
|
data_fields=["heat_power", "heat_energy", "flow_rate",
|
|
|
|
|
|
"supply_temp", "return_temp"]),
|
2026-04-01 13:36:06 +08:00
|
|
|
|
DeviceType(code="water_meter", name="水表", icon="water-meter",
|
|
|
|
|
|
data_fields=["flow_rate", "total_flow"]),
|
|
|
|
|
|
]
|
|
|
|
|
|
session.add_all(device_types)
|
|
|
|
|
|
|
2026-04-02 18:46:42 +08:00
|
|
|
|
# =====================================================================
|
|
|
|
|
|
# 4. 设备分组 (hierarchical: Campus -> subsystems)
|
|
|
|
|
|
# =====================================================================
|
2026-04-01 13:36:06 +08:00
|
|
|
|
groups = [
|
2026-04-02 18:46:42 +08:00
|
|
|
|
DeviceGroup(id=1, name="天普大兴园区", location="北京市大兴区", description="园区总节点"),
|
|
|
|
|
|
DeviceGroup(id=2, name="东楼", parent_id=1, location="天普大楼东侧"),
|
|
|
|
|
|
DeviceGroup(id=3, name="西楼", parent_id=1, location="天普大楼西侧"),
|
|
|
|
|
|
DeviceGroup(id=4, name="光伏系统", parent_id=1, location="天普大楼屋顶"),
|
|
|
|
|
|
DeviceGroup(id=5, name="热泵系统", parent_id=1, location="天普大楼机房"),
|
|
|
|
|
|
DeviceGroup(id=6, name="电力计量", parent_id=1, location="天普大楼配电室"),
|
|
|
|
|
|
DeviceGroup(id=7, name="环境监测", parent_id=1, location="天普大楼各楼层"),
|
2026-04-01 13:36:06 +08:00
|
|
|
|
]
|
|
|
|
|
|
session.add_all(groups)
|
|
|
|
|
|
|
2026-04-02 18:46:42 +08:00
|
|
|
|
# Flush to satisfy FK constraints before inserting devices
|
2026-04-01 13:36:06 +08:00
|
|
|
|
await session.flush()
|
|
|
|
|
|
|
2026-04-02 18:46:42 +08:00
|
|
|
|
# =====================================================================
|
|
|
|
|
|
# 5. 天普实际设备 (19 devices total)
|
|
|
|
|
|
# =====================================================================
|
2026-04-01 13:36:06 +08:00
|
|
|
|
devices = [
|
2026-04-02 18:46:42 +08:00
|
|
|
|
# --- 光伏逆变器 - 3台华为SUN2000-110KTL-M0 ---
|
|
|
|
|
|
Device(name="东楼逆变器1", code="INV-01", device_type="pv_inverter", group_id=4,
|
2026-04-01 13:36:06 +08:00
|
|
|
|
model="SUN2000-110KTL-M0", manufacturer="华为", rated_power=110,
|
|
|
|
|
|
location="东楼屋顶", protocol="http_api", collect_interval=15,
|
|
|
|
|
|
connection_params={"api_type": "fusionsolar", "station_code": "NE=12345"}),
|
2026-04-02 18:46:42 +08:00
|
|
|
|
Device(name="东楼逆变器2", code="INV-02", device_type="pv_inverter", group_id=4,
|
2026-04-01 13:36:06 +08:00
|
|
|
|
model="SUN2000-110KTL-M0", manufacturer="华为", rated_power=110,
|
|
|
|
|
|
location="东楼屋顶", protocol="http_api", collect_interval=15),
|
2026-04-02 18:46:42 +08:00
|
|
|
|
Device(name="西楼逆变器1", code="INV-03", device_type="pv_inverter", group_id=4,
|
2026-04-01 13:36:06 +08:00
|
|
|
|
model="SUN2000-110KTL-M0", manufacturer="华为", rated_power=110,
|
|
|
|
|
|
location="西楼屋顶", protocol="http_api", collect_interval=15),
|
|
|
|
|
|
|
2026-04-02 18:46:42 +08:00
|
|
|
|
# --- 热泵机组 - 4台 ---
|
|
|
|
|
|
Device(name="热泵机组1", code="HP-01", device_type="heat_pump", group_id=5,
|
2026-04-01 13:36:06 +08:00
|
|
|
|
rated_power=35, location="大楼机房", protocol="modbus_rtu", collect_interval=15,
|
|
|
|
|
|
connection_params={"dtu_id": "2225000009", "slave_id": 1}),
|
2026-04-02 18:46:42 +08:00
|
|
|
|
Device(name="热泵机组2", code="HP-02", device_type="heat_pump", group_id=5,
|
2026-04-01 13:36:06 +08:00
|
|
|
|
rated_power=35, location="大楼机房", protocol="modbus_rtu", collect_interval=15,
|
|
|
|
|
|
connection_params={"dtu_id": "2225000009", "slave_id": 2}),
|
2026-04-02 18:46:42 +08:00
|
|
|
|
Device(name="热泵机组3", code="HP-03", device_type="heat_pump", group_id=5,
|
2026-04-01 13:36:06 +08:00
|
|
|
|
rated_power=35, location="大楼机房", protocol="modbus_rtu", collect_interval=15,
|
|
|
|
|
|
connection_params={"dtu_id": "2225000009", "slave_id": 3}),
|
2026-04-02 18:46:42 +08:00
|
|
|
|
Device(name="热泵机组4", code="HP-04", device_type="heat_pump", group_id=5,
|
2026-04-01 13:36:06 +08:00
|
|
|
|
rated_power=35, location="大楼机房", protocol="modbus_rtu", collect_interval=15,
|
|
|
|
|
|
connection_params={"dtu_id": "2225000009", "slave_id": 4}),
|
|
|
|
|
|
|
2026-04-02 18:46:42 +08:00
|
|
|
|
# --- 电表 ---
|
|
|
|
|
|
Device(name="关口电表(余电上网)", code="METER-GRID", device_type="meter", group_id=6,
|
|
|
|
|
|
model="威胜", serial_number="3462847657", location="配电室",
|
|
|
|
|
|
protocol="dlt645", collect_interval=60,
|
2026-04-01 13:36:06 +08:00
|
|
|
|
connection_params={"dtu_id": "infrared", "ratio": 1000}),
|
2026-04-02 18:46:42 +08:00
|
|
|
|
Device(name="并网电表(光伏总发电)", code="METER-PV", device_type="meter", group_id=6,
|
|
|
|
|
|
model="杭州炬华", serial_number="3422994056", location="配电室",
|
|
|
|
|
|
protocol="dlt645", collect_interval=60,
|
2026-04-01 13:36:06 +08:00
|
|
|
|
connection_params={"dtu_id": "infrared", "ct_ratio": "600/5"}),
|
2026-04-02 18:46:42 +08:00
|
|
|
|
Device(name="热泵电表", code="METER-HP", device_type="meter", group_id=6,
|
2026-04-01 13:36:06 +08:00
|
|
|
|
location="机房热泵控制柜", protocol="modbus_rtu", collect_interval=60,
|
|
|
|
|
|
connection_params={"dtu_id": "2225000003"}),
|
2026-04-02 18:46:42 +08:00
|
|
|
|
Device(name="循环水泵电表", code="METER-PUMP", device_type="meter", group_id=6,
|
2026-04-01 13:36:06 +08:00
|
|
|
|
location="机房水泵配电柜", protocol="modbus_rtu", collect_interval=60,
|
|
|
|
|
|
connection_params={"dtu_id": "2225000002"}),
|
|
|
|
|
|
|
2026-04-02 18:46:42 +08:00
|
|
|
|
# --- 热量表 ---
|
|
|
|
|
|
Device(name="主管热量表", code="HM-01", device_type="heat_meter", group_id=5,
|
2026-04-01 13:36:06 +08:00
|
|
|
|
location="机房中部主管", protocol="modbus_rtu", collect_interval=60,
|
|
|
|
|
|
connection_params={"dtu_id": "2225000001"}),
|
|
|
|
|
|
|
2026-04-02 18:46:42 +08:00
|
|
|
|
# --- 温湿度传感器 ---
|
|
|
|
|
|
Device(name="一楼东厅温湿度", code="TH-01", device_type="sensor", group_id=7,
|
2026-04-01 13:36:06 +08:00
|
|
|
|
location="大楼一楼东厅", protocol="mqtt", collect_interval=60,
|
2026-04-02 18:46:42 +08:00
|
|
|
|
connection_params={"dtu_id": "2225000007"},
|
|
|
|
|
|
metadata_={"area": "一楼东展厅风管上"}),
|
|
|
|
|
|
Device(name="一楼西厅温湿度", code="TH-02", device_type="sensor", group_id=7,
|
2026-04-01 13:36:06 +08:00
|
|
|
|
location="大楼一楼西厅", protocol="mqtt", collect_interval=60,
|
2026-04-02 18:46:42 +08:00
|
|
|
|
connection_params={"dtu_id": "2225000006"},
|
|
|
|
|
|
metadata_={"area": "一楼西厅中西风管上"}),
|
|
|
|
|
|
Device(name="二楼西厅温湿度", code="TH-03", device_type="sensor", group_id=7,
|
2026-04-01 13:36:06 +08:00
|
|
|
|
location="大楼二楼西厅", protocol="mqtt", collect_interval=60,
|
2026-04-02 18:46:42 +08:00
|
|
|
|
connection_params={"dtu_id": "2225000005"},
|
|
|
|
|
|
metadata_={"area": "财务门口西侧"}),
|
|
|
|
|
|
Device(name="二楼东厅温湿度", code="TH-04", device_type="sensor", group_id=7,
|
2026-04-01 13:36:06 +08:00
|
|
|
|
location="大楼二楼东厅", protocol="mqtt", collect_interval=60,
|
2026-04-02 18:46:42 +08:00
|
|
|
|
connection_params={"dtu_id": "2225000004"},
|
|
|
|
|
|
metadata_={"area": "英豪对过"}),
|
|
|
|
|
|
Device(name="机房室外温湿度", code="TH-05", device_type="sensor", group_id=7,
|
2026-04-01 13:36:06 +08:00
|
|
|
|
location="机房热泵控制柜", protocol="mqtt", collect_interval=60,
|
2026-04-02 18:46:42 +08:00
|
|
|
|
connection_params={"dtu_id": "2225000008"},
|
|
|
|
|
|
metadata_={"area": "机房门口", "type": "outdoor"}),
|
2026-04-01 13:36:06 +08:00
|
|
|
|
|
2026-04-02 18:46:42 +08:00
|
|
|
|
# --- 水表 ---
|
|
|
|
|
|
Device(name="补水水表", code="WM-01", device_type="water_meter", group_id=5,
|
2026-04-01 13:36:06 +08:00
|
|
|
|
location="机房软水器补水管", protocol="image", collect_interval=300,
|
|
|
|
|
|
connection_params={"type": "smart_capture"}),
|
|
|
|
|
|
]
|
|
|
|
|
|
session.add_all(devices)
|
2026-04-02 18:46:42 +08:00
|
|
|
|
await session.flush()
|
|
|
|
|
|
|
|
|
|
|
|
# =====================================================================
|
|
|
|
|
|
# 6. 告警规则
|
|
|
|
|
|
# =====================================================================
|
|
|
|
|
|
alarm_rules = [
|
|
|
|
|
|
AlarmRule(
|
|
|
|
|
|
name="光伏功率过低告警",
|
|
|
|
|
|
device_type="pv_inverter",
|
|
|
|
|
|
data_type="power",
|
|
|
|
|
|
condition="lt",
|
|
|
|
|
|
threshold=5.0,
|
|
|
|
|
|
duration=1800,
|
|
|
|
|
|
severity="warning",
|
|
|
|
|
|
notify_channels=["app", "wechat"],
|
|
|
|
|
|
is_active=True,
|
|
|
|
|
|
),
|
|
|
|
|
|
AlarmRule(
|
|
|
|
|
|
name="热泵COP过低告警",
|
|
|
|
|
|
device_type="heat_pump",
|
|
|
|
|
|
data_type="cop",
|
|
|
|
|
|
condition="lt",
|
|
|
|
|
|
threshold=2.0,
|
|
|
|
|
|
duration=600,
|
|
|
|
|
|
severity="major",
|
|
|
|
|
|
notify_channels=["app", "sms", "wechat"],
|
|
|
|
|
|
is_active=True,
|
|
|
|
|
|
),
|
|
|
|
|
|
AlarmRule(
|
|
|
|
|
|
name="室内温度超限告警",
|
|
|
|
|
|
device_type="sensor",
|
|
|
|
|
|
data_type="temperature",
|
|
|
|
|
|
condition="range_out",
|
|
|
|
|
|
threshold_high=30.0,
|
|
|
|
|
|
threshold_low=16.0,
|
|
|
|
|
|
duration=300,
|
|
|
|
|
|
severity="warning",
|
|
|
|
|
|
notify_channels=["app"],
|
|
|
|
|
|
is_active=True,
|
|
|
|
|
|
),
|
|
|
|
|
|
AlarmRule(
|
|
|
|
|
|
name="电表通信中断告警",
|
|
|
|
|
|
device_type="meter",
|
|
|
|
|
|
data_type="power",
|
|
|
|
|
|
condition="eq",
|
|
|
|
|
|
threshold=0.0,
|
|
|
|
|
|
duration=3600,
|
|
|
|
|
|
severity="critical",
|
feat: add system settings, audit log, device detail, dark mode, i18n, email notifications
System Management:
- System Settings page with 8 configurable parameters (admin only)
- Audit Log page with filterable table (user, action, resource, date range)
- Audit logging wired into auth, devices, users, alarms, reports API handlers
- SystemSetting model + migration (002)
Device Detail:
- Dedicated /devices/:id page with 4 tabs (realtime, historical trends, alarm history, device info)
- ECharts historical charts with granularity/time range selectors
- Device name clickable in Devices and Monitoring tables → navigates to detail
Email & Scheduling:
- Email service with SMTP support (STARTTLS/SSL/plain)
- Alarm email notification with professional HTML template
- Report scheduler using APScheduler for cron-based auto-generation
- Scheduled report task seeded (daily at 8am)
UI Enhancements:
- Dark mode toggle (persisted to localStorage, Ant Design darkAlgorithm)
- Data comparison view in Analysis page (dual date range, side-by-side metrics)
- i18n framework (i18next) with zh/en translations for menu and common UI
- Language switcher in header (中文/English)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 19:42:22 +08:00
|
|
|
|
notify_channels=["app", "sms", "wechat", "email"],
|
|
|
|
|
|
notify_targets={"emails": ["admin@tianpu.com", "energy@tianpu.com"]},
|
2026-04-02 18:46:42 +08:00
|
|
|
|
is_active=True,
|
|
|
|
|
|
),
|
|
|
|
|
|
AlarmRule(
|
|
|
|
|
|
name="热泵功率过载告警",
|
|
|
|
|
|
device_type="heat_pump",
|
|
|
|
|
|
data_type="power",
|
|
|
|
|
|
condition="gt",
|
|
|
|
|
|
threshold=38.0,
|
|
|
|
|
|
duration=300,
|
|
|
|
|
|
severity="critical",
|
feat: add system settings, audit log, device detail, dark mode, i18n, email notifications
System Management:
- System Settings page with 8 configurable parameters (admin only)
- Audit Log page with filterable table (user, action, resource, date range)
- Audit logging wired into auth, devices, users, alarms, reports API handlers
- SystemSetting model + migration (002)
Device Detail:
- Dedicated /devices/:id page with 4 tabs (realtime, historical trends, alarm history, device info)
- ECharts historical charts with granularity/time range selectors
- Device name clickable in Devices and Monitoring tables → navigates to detail
Email & Scheduling:
- Email service with SMTP support (STARTTLS/SSL/plain)
- Alarm email notification with professional HTML template
- Report scheduler using APScheduler for cron-based auto-generation
- Scheduled report task seeded (daily at 8am)
UI Enhancements:
- Dark mode toggle (persisted to localStorage, Ant Design darkAlgorithm)
- Data comparison view in Analysis page (dual date range, side-by-side metrics)
- i18n framework (i18next) with zh/en translations for menu and common UI
- Language switcher in header (中文/English)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 19:42:22 +08:00
|
|
|
|
notify_channels=["app", "sms", "email"],
|
|
|
|
|
|
notify_targets={"emails": ["admin@tianpu.com"]},
|
2026-04-02 18:46:42 +08:00
|
|
|
|
is_active=True,
|
|
|
|
|
|
),
|
|
|
|
|
|
AlarmRule(
|
|
|
|
|
|
name="光伏逆变器过温告警",
|
|
|
|
|
|
device_type="pv_inverter",
|
|
|
|
|
|
data_type="temperature",
|
|
|
|
|
|
condition="gt",
|
|
|
|
|
|
threshold=65.0,
|
|
|
|
|
|
duration=120,
|
|
|
|
|
|
severity="major",
|
|
|
|
|
|
notify_channels=["app", "sms"],
|
|
|
|
|
|
is_active=True,
|
|
|
|
|
|
),
|
|
|
|
|
|
]
|
|
|
|
|
|
session.add_all(alarm_rules)
|
2026-04-01 13:36:06 +08:00
|
|
|
|
|
2026-04-02 18:46:42 +08:00
|
|
|
|
# =====================================================================
|
|
|
|
|
|
# 7. 碳排放因子
|
|
|
|
|
|
# =====================================================================
|
2026-04-01 13:36:06 +08:00
|
|
|
|
factors = [
|
|
|
|
|
|
EmissionFactor(name="华北电网排放因子", energy_type="electricity", factor=0.8843,
|
2026-04-02 18:46:42 +08:00
|
|
|
|
unit="kWh", scope=2, region="north_china",
|
|
|
|
|
|
source="生态环境部2023", year=2023),
|
2026-04-01 13:36:06 +08:00
|
|
|
|
EmissionFactor(name="天然气排放因子", energy_type="natural_gas", factor=2.162,
|
|
|
|
|
|
unit="m³", scope=1, source="IPCC 2006", year=2006),
|
|
|
|
|
|
EmissionFactor(name="柴油排放因子", energy_type="diesel", factor=2.63,
|
|
|
|
|
|
unit="L", scope=1, source="IPCC 2006", year=2006),
|
|
|
|
|
|
EmissionFactor(name="光伏减排因子", energy_type="pv_generation", factor=0.8843,
|
2026-04-02 18:46:42 +08:00
|
|
|
|
unit="kWh", scope=2, region="north_china",
|
|
|
|
|
|
source="等量替代电网电力", year=2023),
|
2026-04-01 13:36:06 +08:00
|
|
|
|
EmissionFactor(name="热泵节能减排因子", energy_type="heat_pump_saving", factor=0.8843,
|
2026-04-02 18:46:42 +08:00
|
|
|
|
unit="kWh", scope=2, region="north_china",
|
|
|
|
|
|
source="相比电加热节省的电量", year=2023),
|
2026-04-01 13:36:06 +08:00
|
|
|
|
]
|
|
|
|
|
|
session.add_all(factors)
|
|
|
|
|
|
|
2026-04-02 18:46:42 +08:00
|
|
|
|
# =====================================================================
|
|
|
|
|
|
# 8. 预置报表模板
|
|
|
|
|
|
# =====================================================================
|
2026-04-01 13:36:06 +08:00
|
|
|
|
templates = [
|
2026-04-02 18:46:42 +08:00
|
|
|
|
ReportTemplate(
|
|
|
|
|
|
name="日报", report_type="daily", is_system=True,
|
|
|
|
|
|
description="每日能源运行日报,包含发用电量、自消纳率、热泵COP、碳排放",
|
|
|
|
|
|
fields=[
|
|
|
|
|
|
{"key": "total_consumption", "label": "总用电量", "unit": "kWh"},
|
|
|
|
|
|
{"key": "pv_generation", "label": "光伏发电量", "unit": "kWh"},
|
|
|
|
|
|
{"key": "self_use_rate", "label": "自消纳率", "unit": "%"},
|
|
|
|
|
|
{"key": "heatpump_energy", "label": "热泵用电", "unit": "kWh"},
|
|
|
|
|
|
{"key": "avg_cop", "label": "平均COP"},
|
|
|
|
|
|
{"key": "carbon_emission", "label": "碳排放", "unit": "kgCO2"},
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
ReportTemplate(
|
|
|
|
|
|
name="月报", report_type="monthly", is_system=True,
|
|
|
|
|
|
description="每月能源运行月报,包含能耗趋势、电费、碳排放与减排",
|
|
|
|
|
|
fields=[
|
|
|
|
|
|
{"key": "total_consumption", "label": "总用电量", "unit": "kWh"},
|
|
|
|
|
|
{"key": "pv_generation", "label": "光伏发电量", "unit": "kWh"},
|
|
|
|
|
|
{"key": "grid_import", "label": "电网购电", "unit": "kWh"},
|
|
|
|
|
|
{"key": "cost", "label": "电费", "unit": "元"},
|
|
|
|
|
|
{"key": "carbon_emission", "label": "碳排放", "unit": "tCO2"},
|
|
|
|
|
|
{"key": "carbon_reduction", "label": "碳减排", "unit": "tCO2"},
|
|
|
|
|
|
],
|
|
|
|
|
|
time_granularity="day",
|
|
|
|
|
|
),
|
|
|
|
|
|
ReportTemplate(
|
|
|
|
|
|
name="设备运行报告", report_type="custom", is_system=True,
|
|
|
|
|
|
description="设备运行状态汇总,包含运行时长、能耗、告警统计",
|
|
|
|
|
|
fields=[
|
|
|
|
|
|
{"key": "device_name", "label": "设备名称"},
|
|
|
|
|
|
{"key": "operating_hours", "label": "运行时长", "unit": "h"},
|
|
|
|
|
|
{"key": "energy_consumption", "label": "能耗", "unit": "kWh"},
|
|
|
|
|
|
{"key": "avg_power", "label": "平均功率", "unit": "kW"},
|
|
|
|
|
|
{"key": "alarm_count", "label": "告警次数"},
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
2026-04-01 13:36:06 +08:00
|
|
|
|
]
|
|
|
|
|
|
session.add_all(templates)
|
|
|
|
|
|
|
2026-04-02 18:46:42 +08:00
|
|
|
|
await session.flush()
|
|
|
|
|
|
|
feat: add system settings, audit log, device detail, dark mode, i18n, email notifications
System Management:
- System Settings page with 8 configurable parameters (admin only)
- Audit Log page with filterable table (user, action, resource, date range)
- Audit logging wired into auth, devices, users, alarms, reports API handlers
- SystemSetting model + migration (002)
Device Detail:
- Dedicated /devices/:id page with 4 tabs (realtime, historical trends, alarm history, device info)
- ECharts historical charts with granularity/time range selectors
- Device name clickable in Devices and Monitoring tables → navigates to detail
Email & Scheduling:
- Email service with SMTP support (STARTTLS/SSL/plain)
- Alarm email notification with professional HTML template
- Report scheduler using APScheduler for cron-based auto-generation
- Scheduled report task seeded (daily at 8am)
UI Enhancements:
- Dark mode toggle (persisted to localStorage, Ant Design darkAlgorithm)
- Data comparison view in Analysis page (dual date range, side-by-side metrics)
- i18n framework (i18next) with zh/en translations for menu and common UI
- Language switcher in header (中文/English)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 19:42:22 +08:00
|
|
|
|
# =====================================================================
|
|
|
|
|
|
# 8b. 定时报表任务 (daily report at 8am)
|
|
|
|
|
|
# =====================================================================
|
|
|
|
|
|
report_tasks = [
|
|
|
|
|
|
ReportTask(
|
|
|
|
|
|
template_id=1, # 日报模板
|
|
|
|
|
|
name="每日能源运行日报",
|
|
|
|
|
|
schedule="0 8 * * *",
|
|
|
|
|
|
recipients=["admin@tianpu.com"],
|
|
|
|
|
|
export_format="xlsx",
|
|
|
|
|
|
status="pending",
|
|
|
|
|
|
is_active=True,
|
|
|
|
|
|
created_by=1,
|
|
|
|
|
|
),
|
|
|
|
|
|
]
|
|
|
|
|
|
session.add_all(report_tasks)
|
|
|
|
|
|
await session.flush()
|
|
|
|
|
|
|
2026-04-02 18:46:42 +08:00
|
|
|
|
# =====================================================================
|
|
|
|
|
|
# 9. 历史告警事件 (15 events over last 7 days)
|
|
|
|
|
|
# =====================================================================
|
|
|
|
|
|
now = datetime.now(timezone.utc)
|
|
|
|
|
|
|
|
|
|
|
|
# We need device IDs and rule IDs — query them back
|
|
|
|
|
|
dev_result = await session.execute(select(Device))
|
|
|
|
|
|
all_devices = {d.code: d.id for d in dev_result.scalars().all()}
|
|
|
|
|
|
|
|
|
|
|
|
rule_result = await session.execute(select(AlarmRule))
|
|
|
|
|
|
all_rules = {r.name: r for r in rule_result.scalars().all()}
|
|
|
|
|
|
|
|
|
|
|
|
r_pv_low = all_rules["光伏功率过低告警"]
|
|
|
|
|
|
r_hp_cop = all_rules["热泵COP过低告警"]
|
|
|
|
|
|
r_temp = all_rules["室内温度超限告警"]
|
|
|
|
|
|
r_meter = all_rules["电表通信中断告警"]
|
|
|
|
|
|
r_hp_overload = all_rules["热泵功率过载告警"]
|
|
|
|
|
|
r_pv_overtemp = all_rules["光伏逆变器过温告警"]
|
|
|
|
|
|
|
|
|
|
|
|
alarm_events = [
|
|
|
|
|
|
# --- 7 days ago: PV low power (resolved) ---
|
|
|
|
|
|
AlarmEvent(
|
|
|
|
|
|
rule_id=r_pv_low.id, device_id=all_devices["INV-01"],
|
|
|
|
|
|
severity="warning", title="光伏功率过低告警",
|
|
|
|
|
|
description="当前值 2.3,阈值 5.0", value=2.3, threshold=5.0,
|
|
|
|
|
|
status="resolved", resolve_note="云层过境后恢复正常",
|
|
|
|
|
|
triggered_at=now - timedelta(days=7, hours=3),
|
|
|
|
|
|
resolved_at=now - timedelta(days=7, hours=2, minutes=45),
|
|
|
|
|
|
),
|
|
|
|
|
|
# --- 6 days ago: Heat pump COP low (resolved) ---
|
|
|
|
|
|
AlarmEvent(
|
|
|
|
|
|
rule_id=r_hp_cop.id, device_id=all_devices["HP-01"],
|
|
|
|
|
|
severity="major", title="热泵COP过低告警",
|
|
|
|
|
|
description="当前值 1.6,阈值 2.0", value=1.6, threshold=2.0,
|
|
|
|
|
|
status="resolved", resolve_note="设备恢复正常",
|
|
|
|
|
|
triggered_at=now - timedelta(days=6, hours=10),
|
|
|
|
|
|
resolved_at=now - timedelta(days=6, hours=9, minutes=30),
|
|
|
|
|
|
),
|
|
|
|
|
|
# --- 5 days ago: Temperature out of range (resolved) ---
|
|
|
|
|
|
AlarmEvent(
|
|
|
|
|
|
rule_id=r_temp.id, device_id=all_devices["TH-01"],
|
|
|
|
|
|
severity="warning", title="室内温度超限告警",
|
|
|
|
|
|
description="当前值 31.2,阈值 [16.0, 30.0]", value=31.2, threshold=30.0,
|
|
|
|
|
|
status="resolved", resolve_note="已调节空调温度",
|
|
|
|
|
|
triggered_at=now - timedelta(days=5, hours=14),
|
|
|
|
|
|
resolved_at=now - timedelta(days=5, hours=13, minutes=20),
|
|
|
|
|
|
),
|
|
|
|
|
|
# --- 5 days ago: Meter communication lost (resolved) ---
|
|
|
|
|
|
AlarmEvent(
|
|
|
|
|
|
rule_id=r_meter.id, device_id=all_devices["METER-HP"],
|
|
|
|
|
|
severity="critical", title="电表通信中断告警",
|
|
|
|
|
|
description="当前值 0.0,阈值 0.0", value=0.0, threshold=0.0,
|
|
|
|
|
|
status="resolved", resolve_note="已派人检修,通信恢复",
|
|
|
|
|
|
triggered_at=now - timedelta(days=5, hours=8),
|
|
|
|
|
|
resolved_at=now - timedelta(days=5, hours=6),
|
|
|
|
|
|
),
|
|
|
|
|
|
# --- 4 days ago: HP overload (resolved) ---
|
|
|
|
|
|
AlarmEvent(
|
|
|
|
|
|
rule_id=r_hp_overload.id, device_id=all_devices["HP-02"],
|
|
|
|
|
|
severity="critical", title="热泵功率过载告警",
|
|
|
|
|
|
description="当前值 40.2,阈值 38.0", value=40.2, threshold=38.0,
|
|
|
|
|
|
status="resolved", resolve_note="负荷降低后自动恢复",
|
|
|
|
|
|
triggered_at=now - timedelta(days=4, hours=16),
|
|
|
|
|
|
resolved_at=now - timedelta(days=4, hours=15, minutes=40),
|
|
|
|
|
|
),
|
|
|
|
|
|
# --- 4 days ago: PV overtemp (resolved) ---
|
|
|
|
|
|
AlarmEvent(
|
|
|
|
|
|
rule_id=r_pv_overtemp.id, device_id=all_devices["INV-01"],
|
|
|
|
|
|
severity="major", title="光伏逆变器过温告警",
|
|
|
|
|
|
description="当前值 68.5,阈值 65.0", value=68.5, threshold=65.0,
|
|
|
|
|
|
status="resolved", resolve_note="自动恢复",
|
|
|
|
|
|
triggered_at=now - timedelta(days=4, hours=13),
|
|
|
|
|
|
resolved_at=now - timedelta(days=4, hours=12, minutes=45),
|
|
|
|
|
|
),
|
|
|
|
|
|
# --- 3 days ago: PV low power again (resolved) ---
|
|
|
|
|
|
AlarmEvent(
|
|
|
|
|
|
rule_id=r_pv_low.id, device_id=all_devices["INV-03"],
|
|
|
|
|
|
severity="warning", title="光伏功率过低告警",
|
|
|
|
|
|
description="当前值 1.8,阈值 5.0", value=1.8, threshold=5.0,
|
|
|
|
|
|
status="resolved", resolve_note="自动恢复",
|
|
|
|
|
|
triggered_at=now - timedelta(days=3, hours=11),
|
|
|
|
|
|
resolved_at=now - timedelta(days=3, hours=10, minutes=48),
|
|
|
|
|
|
),
|
|
|
|
|
|
# --- 2 days ago: Temperature sensor (acknowledged) ---
|
|
|
|
|
|
AlarmEvent(
|
|
|
|
|
|
rule_id=r_temp.id, device_id=all_devices["TH-03"],
|
|
|
|
|
|
severity="warning", title="室内温度超限告警",
|
|
|
|
|
|
description="当前值 15.2,阈值 [16.0, 30.0]", value=15.2, threshold=16.0,
|
|
|
|
|
|
status="resolved", resolve_note="供暖开启后恢复",
|
|
|
|
|
|
triggered_at=now - timedelta(days=2, hours=7),
|
|
|
|
|
|
acknowledged_at=now - timedelta(days=2, hours=6, minutes=50),
|
|
|
|
|
|
resolved_at=now - timedelta(days=2, hours=5),
|
|
|
|
|
|
),
|
|
|
|
|
|
# --- 2 days ago: HP COP low (acknowledged, then resolved) ---
|
|
|
|
|
|
AlarmEvent(
|
|
|
|
|
|
rule_id=r_hp_cop.id, device_id=all_devices["HP-03"],
|
|
|
|
|
|
severity="major", title="热泵COP过低告警",
|
|
|
|
|
|
description="当前值 1.4,阈值 2.0", value=1.4, threshold=2.0,
|
|
|
|
|
|
status="resolved", resolve_note="已派人检修",
|
|
|
|
|
|
triggered_at=now - timedelta(days=2, hours=15),
|
|
|
|
|
|
acknowledged_at=now - timedelta(days=2, hours=14, minutes=30),
|
|
|
|
|
|
resolved_at=now - timedelta(days=2, hours=12),
|
|
|
|
|
|
),
|
|
|
|
|
|
# --- 1 day ago: PV overtemp (resolved) ---
|
|
|
|
|
|
AlarmEvent(
|
|
|
|
|
|
rule_id=r_pv_overtemp.id, device_id=all_devices["INV-02"],
|
|
|
|
|
|
severity="major", title="光伏逆变器过温告警",
|
|
|
|
|
|
description="当前值 67.1,阈值 65.0", value=67.1, threshold=65.0,
|
|
|
|
|
|
status="resolved", resolve_note="设备恢复正常",
|
|
|
|
|
|
triggered_at=now - timedelta(days=1, hours=14),
|
|
|
|
|
|
resolved_at=now - timedelta(days=1, hours=13, minutes=30),
|
|
|
|
|
|
),
|
|
|
|
|
|
# --- 1 day ago: HP overload (acknowledged, still active) ---
|
|
|
|
|
|
AlarmEvent(
|
|
|
|
|
|
rule_id=r_hp_overload.id, device_id=all_devices["HP-04"],
|
|
|
|
|
|
severity="critical", title="热泵功率过载告警",
|
|
|
|
|
|
description="当前值 39.5,阈值 38.0", value=39.5, threshold=38.0,
|
|
|
|
|
|
status="acknowledged",
|
|
|
|
|
|
triggered_at=now - timedelta(hours=18),
|
|
|
|
|
|
acknowledged_at=now - timedelta(hours=17),
|
|
|
|
|
|
),
|
|
|
|
|
|
# --- 12 hours ago: Meter communication (active) ---
|
|
|
|
|
|
AlarmEvent(
|
|
|
|
|
|
rule_id=r_meter.id, device_id=all_devices["METER-PUMP"],
|
|
|
|
|
|
severity="critical", title="电表通信中断告警",
|
|
|
|
|
|
description="当前值 0.0,阈值 0.0", value=0.0, threshold=0.0,
|
|
|
|
|
|
status="active",
|
|
|
|
|
|
triggered_at=now - timedelta(hours=12),
|
|
|
|
|
|
),
|
|
|
|
|
|
# --- 6 hours ago: Temperature out of range (active) ---
|
|
|
|
|
|
AlarmEvent(
|
|
|
|
|
|
rule_id=r_temp.id, device_id=all_devices["TH-02"],
|
|
|
|
|
|
severity="warning", title="室内温度超限告警",
|
|
|
|
|
|
description="当前值 31.8,阈值 [16.0, 30.0]", value=31.8, threshold=30.0,
|
|
|
|
|
|
status="active",
|
|
|
|
|
|
triggered_at=now - timedelta(hours=6),
|
|
|
|
|
|
),
|
|
|
|
|
|
# --- 2 hours ago: PV low (active) ---
|
|
|
|
|
|
AlarmEvent(
|
|
|
|
|
|
rule_id=r_pv_low.id, device_id=all_devices["INV-02"],
|
|
|
|
|
|
severity="warning", title="光伏功率过低告警",
|
|
|
|
|
|
description="当前值 3.1,阈值 5.0", value=3.1, threshold=5.0,
|
|
|
|
|
|
status="active",
|
|
|
|
|
|
triggered_at=now - timedelta(hours=2),
|
|
|
|
|
|
),
|
|
|
|
|
|
# --- 30 min ago: HP COP low (active) ---
|
|
|
|
|
|
AlarmEvent(
|
|
|
|
|
|
rule_id=r_hp_cop.id, device_id=all_devices["HP-02"],
|
|
|
|
|
|
severity="major", title="热泵COP过低告警",
|
|
|
|
|
|
description="当前值 1.7,阈值 2.0", value=1.7, threshold=2.0,
|
|
|
|
|
|
status="active",
|
|
|
|
|
|
triggered_at=now - timedelta(minutes=30),
|
|
|
|
|
|
),
|
|
|
|
|
|
]
|
|
|
|
|
|
session.add_all(alarm_events)
|
|
|
|
|
|
|
2026-04-01 13:36:06 +08:00
|
|
|
|
await session.commit()
|
2026-04-02 18:46:42 +08:00
|
|
|
|
|
|
|
|
|
|
print("=" * 60)
|
2026-04-01 13:36:06 +08:00
|
|
|
|
print("Seed data inserted successfully!")
|
2026-04-02 18:46:42 +08:00
|
|
|
|
print("=" * 60)
|
|
|
|
|
|
print(f" - {len(roles)} roles (with permissions)")
|
|
|
|
|
|
print(f" - {len(users)} users (admin/admin123, energy_mgr/tianpu123, operator1/tianpu123)")
|
2026-04-01 13:36:06 +08:00
|
|
|
|
print(f" - {len(device_types)} device types")
|
2026-04-02 18:46:42 +08:00
|
|
|
|
print(f" - {len(groups)} device groups (hierarchical)")
|
2026-04-01 13:36:06 +08:00
|
|
|
|
print(f" - {len(devices)} devices")
|
2026-04-02 18:46:42 +08:00
|
|
|
|
print(f" - {len(alarm_rules)} alarm rules")
|
2026-04-01 13:36:06 +08:00
|
|
|
|
print(f" - {len(factors)} emission factors")
|
|
|
|
|
|
print(f" - {len(templates)} report templates")
|
feat: add system settings, audit log, device detail, dark mode, i18n, email notifications
System Management:
- System Settings page with 8 configurable parameters (admin only)
- Audit Log page with filterable table (user, action, resource, date range)
- Audit logging wired into auth, devices, users, alarms, reports API handlers
- SystemSetting model + migration (002)
Device Detail:
- Dedicated /devices/:id page with 4 tabs (realtime, historical trends, alarm history, device info)
- ECharts historical charts with granularity/time range selectors
- Device name clickable in Devices and Monitoring tables → navigates to detail
Email & Scheduling:
- Email service with SMTP support (STARTTLS/SSL/plain)
- Alarm email notification with professional HTML template
- Report scheduler using APScheduler for cron-based auto-generation
- Scheduled report task seeded (daily at 8am)
UI Enhancements:
- Dark mode toggle (persisted to localStorage, Ant Design darkAlgorithm)
- Data comparison view in Analysis page (dual date range, side-by-side metrics)
- i18n framework (i18next) with zh/en translations for menu and common UI
- Language switcher in header (中文/English)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 19:42:22 +08:00
|
|
|
|
print(f" - {len(report_tasks)} report tasks (scheduled)")
|
2026-04-02 18:46:42 +08:00
|
|
|
|
print(f" - {len(alarm_events)} alarm events (historical)")
|
|
|
|
|
|
|
|
|
|
|
|
await engine.dispose()
|
2026-04-01 13:36:06 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
asyncio.run(seed())
|