135 lines
4.4 KiB
Python
135 lines
4.4 KiB
Python
#!/usr/bin/env python3
|
|
"""TeamBition 数据拉取入口 - 隐秘之潮项目"""
|
|
|
|
import sys
|
|
import os
|
|
import json
|
|
from datetime import datetime
|
|
|
|
# Add project root to path
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
from tb.tb_config import TBConfig
|
|
from tb.teambition_api import TeambitionAPI
|
|
from tb.task_analyzer import TaskAnalyzer
|
|
from tb.task_manager import TaskManager
|
|
|
|
|
|
def fetch_tb_data(send_to_dingtalk=False, mode="daily"):
|
|
"""拉取 TeamBition 任务数据并分析"""
|
|
config = TBConfig()
|
|
api = TeambitionAPI(config)
|
|
analyzer = TaskAnalyzer(config)
|
|
task_manager = TaskManager(
|
|
os.path.join(os.path.dirname(__file__), "data", "tb", "task_state.json")
|
|
)
|
|
|
|
project_id = config.get("teambition.project_id", "unknown")
|
|
print(f"\n{'='*60}")
|
|
print(f"拉取 TeamBition 任务数据 - 项目: {project_id}")
|
|
print(f"{'='*60}\n")
|
|
|
|
# 1. Export tasks via TeamBition API
|
|
csv_content = api.fetch_tasks()
|
|
|
|
if not csv_content:
|
|
print("获取任务数据失败")
|
|
return None
|
|
|
|
# 2. Save CSV
|
|
output_dir = os.path.join(os.path.dirname(__file__), "data", "tb")
|
|
os.makedirs(output_dir, exist_ok=True)
|
|
csv_path = api.save_csv(csv_content, output_dir=output_dir)
|
|
|
|
if not csv_path:
|
|
print("保存 CSV 失败")
|
|
return None
|
|
|
|
# 3. Analyze
|
|
df = analyzer.load_csv(csv_path)
|
|
if df.empty:
|
|
print("CSV 数据为空")
|
|
return None
|
|
|
|
# 4. Fetch task activities for contribution tracking
|
|
id_col = next(
|
|
(c for c in ["任务 ObjectId", "_id", "id", "taskId", "任务ID"] if c in df.columns),
|
|
None,
|
|
)
|
|
activities = []
|
|
if id_col:
|
|
task_ids = [t for t in df[id_col].dropna().astype(str).tolist() if t and t != "nan"]
|
|
if task_ids:
|
|
print(f"获取 {len(task_ids)} 个任务的动态...")
|
|
try:
|
|
activities = api.fetch_tasks_activities(task_ids)
|
|
except Exception as e:
|
|
print(f"[Warning] 获取动态失败: {e}")
|
|
|
|
# 5. Generate summary
|
|
summary = analyzer.analyze_tasks(df, activities)
|
|
summary_path = os.path.join(output_dir, "summary.json")
|
|
analyzer.save_summary(summary)
|
|
|
|
# 6. Detect changes
|
|
current_tasks = analyzer.get_task_dict(df)
|
|
old_state = task_manager.load_state()
|
|
prev_tasks = old_state.get("tasks", {})
|
|
changes = task_manager.detect_changes(prev_tasks, current_tasks)
|
|
|
|
if changes:
|
|
print("\n[变动检测]")
|
|
for c in changes:
|
|
print(f" {c}")
|
|
else:
|
|
print("\n[变动检测] 无变动")
|
|
|
|
task_manager.save_state(current_tasks)
|
|
|
|
# 7. Optionally send to DingTalk
|
|
if send_to_dingtalk:
|
|
from tb.dingtalk_sender import DingTalkSender
|
|
|
|
dingtalk_config = config.get("dingtalk", {})
|
|
if dingtalk_config.get("enabled"):
|
|
robots = dingtalk_config.get("robots", {})
|
|
user_roles = config.get("user_roles", {})
|
|
user_mobiles = config.get("user_mobiles", {})
|
|
switches = dingtalk_config.get("switches", {})
|
|
|
|
temp_sender = DingTalkSender("")
|
|
report, at_mobiles = temp_sender.format_task_report(
|
|
summary, user_roles, user_mobiles,
|
|
dashboard_url=config.get("dashboard_url"),
|
|
switches=switches,
|
|
)
|
|
|
|
if switches.get("daily_diff", True):
|
|
report = temp_sender.format_diff_report(changes, report)
|
|
|
|
for name, bot_cfg in robots.items():
|
|
webhook = bot_cfg.get("webhook")
|
|
secret = bot_cfg.get("secret")
|
|
if webhook:
|
|
sender = DingTalkSender(webhook)
|
|
sender.send_markdown("Teambition 任务日报", report, secret=secret, at_mobiles=at_mobiles)
|
|
print(f"已发送到钉钉机器人: {name}")
|
|
|
|
# 8. Cleanup CSV
|
|
if csv_path and os.path.exists(csv_path):
|
|
os.remove(csv_path)
|
|
|
|
print(f"\n完成! 共 {summary.get('total_tasks', 0)} 个任务")
|
|
return summary
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser(description="TeamBition 数据拉取")
|
|
parser.add_argument("--dingtalk", action="store_true", help="发送到钉钉群")
|
|
parser.add_argument("--mode", default="daily", choices=["daily", "interval"])
|
|
args = parser.parse_args()
|
|
|
|
fetch_tb_data(send_to_dingtalk=args.dingtalk, mode=args.mode)
|