111 lines
4.0 KiB
Python
111 lines
4.0 KiB
Python
"""把全部周期的年份整体平移 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()
|