| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- #!/usr/bin/env python3
- """只读导出 PKS 全场点位数据到 Excel(每机组一个 sheet,每天固定 10 分钟)。
- 第 1 行为英文字段名,第 2 行为中文名(取自 site_point.ItemDescription)。
- 用法示例:
- python backend/export_pks_daily_10min.py \
- --start 2026-07-12 --end 2026-07-21 \
- --window 16:14:00 --minutes 10 \
- --output 7-8-9号机_20260712-20260721_1614-1624.xlsx
- """
- from __future__ import annotations
- import argparse
- import os
- import re
- import sys
- from datetime import date, datetime, time, timedelta
- from pathlib import Path
- import pymysql
- from openpyxl import Workbook
- from openpyxl.styles import Font
- sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
- from app.config import settings # noqa: E402
- UNITS = ("7#", "8#", "9#")
- SITE_POINT_COLUMNS = tuple(f"YSJ_{index}" for index in range(1, 42))
- SAMPLE_SECONDS = 5
- def connect():
- return pymysql.connect(
- host=settings.db_host,
- port=settings.db_port,
- user=settings.db_user,
- password=settings.db_password,
- database=settings.db_name,
- charset="utf8mb4",
- cursorclass=pymysql.cursors.DictCursor,
- connect_timeout=settings.db_connect_timeout,
- read_timeout=600,
- write_timeout=60,
- autocommit=True,
- )
- def parse_date(value: str) -> date:
- return datetime.strptime(value, "%Y-%m-%d").date()
- def parse_time(value: str) -> time:
- return datetime.strptime(value, "%H:%M:%S").time()
- def unit_number(unit: str) -> str:
- match = re.search(r"([789])", unit)
- if not match:
- raise ValueError(f"无法从 {unit!r} 解析机组号")
- return match.group(1)
- def load_chinese_names(units: list[str]) -> dict[str, dict[str, str]]:
- """读取每个机组 YSJ_1..41 的中文名,返回 {机组: {列名: 中文名}}。"""
- names = [f"YSJ{unit_number(unit)}_{index}" for unit in units for index in range(1, 42)]
- placeholders = ",".join(["%s"] * len(names))
- connection = connect()
- try:
- with connection.cursor() as cursor:
- cursor.execute(
- f"SELECT ItemName, ItemDescription FROM site_point WHERE ItemName IN ({placeholders})",
- names,
- )
- description = {row["ItemName"]: row["ItemDescription"] for row in cursor.fetchall()}
- finally:
- connection.close()
- result: dict[str, dict[str, str]] = {}
- for unit in units:
- unit_names = {}
- for index in range(1, 42):
- column = f"YSJ_{index}"
- item_name = f"YSJ{unit_number(unit)}_{index}"
- unit_names[column] = description.get(item_name) or column
- result[unit] = unit_names
- return result
- def fetch_unit_rows(unit: str, days: list[date], window: time, minutes: int) -> list[dict]:
- """按天查询指定机组固定 10 分钟窗口内的全部点位数据。"""
- columns = ", ".join(f"t.{column}" for column in SITE_POINT_COLUMNS)
- sql = f"""
- SELECT t.sample_time, {columns}
- FROM pks_long_sample t
- INNER JOIN compressor_data_import i ON i.id = t.import_batch_id
- WHERE i.directory_path = %s
- AND i.data_type = 'PKS'
- AND t.sample_time >= %s
- AND t.sample_time <= %s
- ORDER BY t.sample_time
- """
- rows: list[dict] = []
- connection = connect()
- try:
- with connection.cursor() as cursor:
- for day in days:
- start = datetime.combine(day, window)
- end = start + timedelta(minutes=minutes) - timedelta(seconds=SAMPLE_SECONDS)
- cursor.execute(sql, (unit, start, end))
- rows.extend(cursor.fetchall())
- finally:
- connection.close()
- return rows
- def write_sheet(workbook: Workbook, unit: str, chinese_names: dict[str, str], rows: list[dict]) -> None:
- sheet = workbook.create_sheet(title=f"{unit_number(unit)}号机")
- header_en = ["sample_time", *SITE_POINT_COLUMNS]
- header_cn = ["采样时间", *(chinese_names[column] for column in SITE_POINT_COLUMNS)]
- sheet.append(header_en)
- sheet.append(header_cn)
- for cell in sheet[1]:
- cell.font = Font(bold=True)
- for cell in sheet[2]:
- cell.font = Font(bold=True)
- for row in rows:
- sample_time = row["sample_time"]
- values = [sample_time.strftime("%Y-%m-%d %H:%M:%S") if isinstance(sample_time, datetime) else sample_time]
- values.extend(row[column] for column in SITE_POINT_COLUMNS)
- sheet.append(values)
- sheet.freeze_panes = "A3"
- def main() -> None:
- parser = argparse.ArgumentParser(description="只读导出 PKS 全场点位固定 10 分钟数据到 Excel")
- parser.add_argument("--start", default="2026-07-12", help="起始日期 YYYY-MM-DD(含)")
- parser.add_argument("--end", default="2026-07-21", help="结束日期 YYYY-MM-DD(含)")
- parser.add_argument("--window", default="16:14:00", help="每天窗口起始时刻 HH:MM:SS")
- parser.add_argument("--minutes", type=int, default=10, help="每天窗口分钟数")
- parser.add_argument("--units", nargs="+", default=list(UNITS), help="机组列表,默认 7# 8# 9#")
- parser.add_argument(
- "--output",
- default=str(Path(__file__).resolve().parents[1] / "7-8-9号机_20260712-20260721_1614-1624.xlsx"),
- )
- args = parser.parse_args()
- start, end = parse_date(args.start), parse_date(args.end)
- if end < start:
- parser.error("结束日期不能早于起始日期")
- if args.minutes < 1:
- parser.error("窗口分钟数必须大于 0")
- window = parse_time(args.window)
- days = [start + timedelta(days=offset) for offset in range((end - start).days + 1)]
- expected_per_day = args.minutes * 60 // SAMPLE_SECONDS
- print(f"只读模式:导出 {', '.join(args.units)},{start} ~ {end},每天 {args.window} 起 {args.minutes} 分钟")
- print(f"每天应有 {expected_per_day} 条(5 秒采样),共 {len(days)} 天")
- chinese_names = load_chinese_names(args.units)
- workbook = Workbook()
- workbook.remove(workbook.active)
- exported = []
- for unit in args.units:
- rows = fetch_unit_rows(unit, days, window, args.minutes)
- if not rows:
- print(f"{unit}:该时间段无数据,跳过")
- continue
- expected_total = expected_per_day * len(days)
- flag = "OK" if len(rows) == expected_total else "注意"
- print(f"{unit}:{len(rows)} 条(预期 {expected_total})[{flag}]")
- write_sheet(workbook, unit, chinese_names[unit], rows)
- exported.append((unit, len(rows)))
- if not exported:
- parser.error("所选机组在指定时间段均无数据,未生成文件")
- output = Path(args.output)
- output.parent.mkdir(parents=True, exist_ok=True)
- workbook.save(output)
- print(f"输出文件:{output}")
- for unit, count in exported:
- print(f" sheet {unit_number(unit)}号机:{count} 行")
- if __name__ == "__main__":
- main()
|