unis_manager/scripts/shift_year.py

111 lines
4.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

"""把全部周期的年份整体平移 N 年(默认 +1)。
场景:历史数据的年份口径需要调整(例如 2024→2025、2025→2026)。
注意实现要点:
- Period 同时存在 2024 与 2025,必须**按年份从大到小**更新,
否则 2024→2025 会撞上尚未更新的 2025,造成 sort_key 重复。
- Period.label 存的是『8月第4周』这类不含年份的原始文本,无需改动;
带年份的显示名(full_label)是按 year 动态生成的,会自动跟随。
- 执行前会校验 sort_key 是否出现重复,有重复则整体回滚。
用法:
python scripts/shift_year.py --dry-run # 预演
python scripts/shift_year.py # 全部 +1 年
python scripts/shift_year.py --years -1 # 反向:全部 -1 年
"""
from __future__ import annotations
import argparse
import re
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from app.db import SessionLocal # noqa: E402
from app.models import CustomProject, Period # noqa: E402
def shift_date(value: str | None, years: int) -> str | None:
"""把 '2025-08-14' 这类日期字符串的年份平移。"""
if not value:
return value
m = re.match(r"^(\d{4})(.*)$", str(value).strip())
if not m:
return value
return f"{int(m.group(1)) + years}{m.group(2)}"
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--years", type=int, default=1, help="平移年数,默认 +1")
ap.add_argument("--dry-run", action="store_true")
args = ap.parse_args()
if args.years == 0:
print("平移年数为 0,无需处理")
return
db = SessionLocal()
try:
# 按年份从大到小,避免中间态撞车
periods = db.query(Period).order_by(Period.year.desc(), Period.sort_key.desc()).all()
if not periods:
print("没有周期数据")
return
print(f"周期共 {len(periods)} 个,年份平移 {args.years:+d} 年")
print(f"{'原周期':>22} → {'新周期':<22}")
print("-" * 56)
changed = 0
target_keys = []
for p in periods:
new_year = p.year + args.years
new_key = new_year * 10000 + p.month * 100 + p.week
target_keys.append(new_key)
old_label = f"{p.year}年{p.month}月第{p.week}周"
new_label = f"{new_year}年{p.month}月第{p.week}周"
if changed < 6 or changed >= len(periods) - 4:
print(f"{old_label:>22} → {new_label:<22}")
elif changed == 6:
print(f"{'…':>22} {'…'}")
p.year = new_year
p.sort_key = new_key
changed += 1
# 冲突校验
if len(set(target_keys)) != len(target_keys):
db.rollback()
print("!! 平移后出现重复周期,已回滚。请先清理重复的周期数据。")
return
# 定制项目的日期字段同步平移
date_changed = 0
for cp in db.query(CustomProject).all():
for field in ("sign_date", "deliver_date", "revenue_date"):
old = getattr(cp, field)
new = shift_date(old, args.years)
if new != old:
setattr(cp, field, new)
date_changed += 1
if args.dry_run:
db.rollback()
print("-" * 56)
print(f"[dry-run] 将平移 {changed} 个周期、{date_changed} 个日期字段,未写入。")
return
db.commit()
print("-" * 56)
print(f"[ok] 已平移 {changed} 个周期({args.years:+d} 年),日期字段 {date_changed} 个")
latest = db.query(Period).order_by(Period.sort_key.desc()).first()
print(f"[ok] 当前最新周期:{latest.year}年{latest.month}月第{latest.week}周")
print(" 提示:如需回滚,执行 python scripts/shift_year.py --years " + f"{-args.years}")
finally:
db.close()
if __name__ == "__main__":
main()