calendar_query.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. #!/usr/bin/env python3
  2. """
  3. 日程查询脚本 用于查看和管理日程安排.
  4. """
  5. import argparse
  6. import asyncio
  7. import sys
  8. from datetime import datetime, timedelta
  9. from pathlib import Path
  10. from src.mcp.tools.calendar import get_calendar_manager
  11. from src.utils.logging_config import get_logger
  12. # 添加项目根目录到Python路径 - 必须在导入src模块之前
  13. project_root = Path(__file__).parent.parent
  14. sys.path.insert(0, str(project_root))
  15. logger = get_logger(__name__)
  16. class CalendarQueryScript:
  17. """
  18. 日程查询脚本类.
  19. """
  20. def __init__(self):
  21. self.manager = get_calendar_manager()
  22. def format_event_display(self, event, show_details=True):
  23. """
  24. 格式化事件显示.
  25. """
  26. start_dt = datetime.fromisoformat(event.start_time)
  27. end_dt = datetime.fromisoformat(event.end_time)
  28. # 基本信息
  29. time_str = f"{start_dt.strftime('%m/%d %H:%M')} - {end_dt.strftime('%H:%M')}"
  30. basic_info = f"📅 {time_str} | 【{event.category}】{event.title}"
  31. if not show_details:
  32. return basic_info
  33. # 详细信息
  34. details = []
  35. if event.description:
  36. details.append(f" 📝 备注: {event.description}")
  37. # 提醒信息
  38. if event.reminder_minutes > 0:
  39. details.append(f" ⏰ 提醒: 提前{event.reminder_minutes}分钟")
  40. if hasattr(event, "reminder_sent") and event.reminder_sent:
  41. details.append(" ✅ 提醒状态: 已发送")
  42. else:
  43. details.append(" ⏳ 提醒状态: 待发送")
  44. # 时间距离
  45. now = datetime.now()
  46. time_diff = start_dt - now
  47. if time_diff.total_seconds() > 0:
  48. days = time_diff.days
  49. hours = int(time_diff.seconds // 3600)
  50. minutes = int((time_diff.seconds % 3600) // 60)
  51. time_until_parts = []
  52. if days > 0:
  53. time_until_parts.append(f"{days}天")
  54. if hours > 0:
  55. time_until_parts.append(f"{hours}小时")
  56. if minutes > 0:
  57. time_until_parts.append(f"{minutes}分钟")
  58. if time_until_parts:
  59. details.append(f" 🕐 距离开始: {' '.join(time_until_parts)}")
  60. else:
  61. details.append(" 🕐 距离开始: 即将开始")
  62. elif start_dt <= now <= end_dt:
  63. details.append(" 🔴 状态: 正在进行中")
  64. else:
  65. details.append(" ✅ 状态: 已结束")
  66. if details:
  67. return basic_info + "\n" + "\n".join(details)
  68. return basic_info
  69. async def query_today(self):
  70. """
  71. 查询今日日程.
  72. """
  73. print("📅 今日日程安排")
  74. print("=" * 50)
  75. now = datetime.now()
  76. today_start = now.replace(hour=0, minute=0, second=0, microsecond=0)
  77. today_end = today_start + timedelta(days=1)
  78. events = self.manager.get_events(
  79. start_date=today_start.isoformat(), end_date=today_end.isoformat()
  80. )
  81. if not events:
  82. print("🎉 今天没有安排任何日程")
  83. return
  84. print(f"📊 共有 {len(events)} 个日程:\n")
  85. for i, event in enumerate(events, 1):
  86. print(f"{i}. {self.format_event_display(event)}")
  87. if i < len(events):
  88. print()
  89. async def query_tomorrow(self):
  90. """
  91. 查询明日日程.
  92. """
  93. print("📅 明日日程安排")
  94. print("=" * 50)
  95. now = datetime.now()
  96. tomorrow_start = (now + timedelta(days=1)).replace(
  97. hour=0, minute=0, second=0, microsecond=0
  98. )
  99. tomorrow_end = tomorrow_start + timedelta(days=1)
  100. events = self.manager.get_events(
  101. start_date=tomorrow_start.isoformat(), end_date=tomorrow_end.isoformat()
  102. )
  103. if not events:
  104. print("🎉 明天没有安排任何日程")
  105. return
  106. print(f"📊 共有 {len(events)} 个日程:\n")
  107. for i, event in enumerate(events, 1):
  108. print(f"{i}. {self.format_event_display(event)}")
  109. if i < len(events):
  110. print()
  111. async def query_week(self):
  112. """
  113. 查询本周日程.
  114. """
  115. print("📅 本周日程安排")
  116. print("=" * 50)
  117. now = datetime.now()
  118. # 本周一
  119. days_since_monday = now.weekday()
  120. week_start = (now - timedelta(days=days_since_monday)).replace(
  121. hour=0, minute=0, second=0, microsecond=0
  122. )
  123. week_end = week_start + timedelta(days=7)
  124. events = self.manager.get_events(
  125. start_date=week_start.isoformat(), end_date=week_end.isoformat()
  126. )
  127. if not events:
  128. print("🎉 本周没有安排任何日程")
  129. return
  130. print(f"📊 共有 {len(events)} 个日程:\n")
  131. # 按日期分组显示
  132. events_by_date = {}
  133. for event in events:
  134. event_date = datetime.fromisoformat(event.start_time).date()
  135. if event_date not in events_by_date:
  136. events_by_date[event_date] = []
  137. events_by_date[event_date].append(event)
  138. for date in sorted(events_by_date.keys()):
  139. weekday = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"][
  140. date.weekday()
  141. ]
  142. print(f"📆 {date.strftime('%m月%d日')} ({weekday})")
  143. print("-" * 30)
  144. for event in events_by_date[date]:
  145. print(f" {self.format_event_display(event, show_details=False)}")
  146. print()
  147. async def query_upcoming(self, hours=24):
  148. """
  149. 查询即将到来的日程.
  150. """
  151. print(f"📅 未来 {hours} 小时内的日程")
  152. print("=" * 50)
  153. now = datetime.now()
  154. end_time = now + timedelta(hours=hours)
  155. events = self.manager.get_events(
  156. start_date=now.isoformat(), end_date=end_time.isoformat()
  157. )
  158. if not events:
  159. print(f"🎉 未来 {hours} 小时内没有安排任何日程")
  160. return
  161. print(f"📊 共有 {len(events)} 个日程:\n")
  162. for i, event in enumerate(events, 1):
  163. print(f"{i}. {self.format_event_display(event)}")
  164. if i < len(events):
  165. print()
  166. async def query_by_category(self, category=None):
  167. """
  168. 按分类查询日程.
  169. """
  170. if category:
  171. print(f"📅 【{category}】分类的日程")
  172. print("=" * 50)
  173. events = self.manager.get_events(category=category)
  174. if not events:
  175. print(f"🎉 【{category}】分类下没有任何日程")
  176. return
  177. print(f"📊 共有 {len(events)} 个日程:\n")
  178. for i, event in enumerate(events, 1):
  179. print(f"{i}. {self.format_event_display(event)}")
  180. if i < len(events):
  181. print()
  182. else:
  183. print("📅 所有分类统计")
  184. print("=" * 50)
  185. categories = self.manager.get_categories()
  186. if not categories:
  187. print("🎉 暂无任何分类")
  188. return
  189. print("📊 分类列表:")
  190. for i, cat in enumerate(categories, 1):
  191. # 统计每个分类的事件数量
  192. events = self.manager.get_events(category=cat)
  193. print(f"{i}. 【{cat}】- {len(events)} 个日程")
  194. async def query_all(self):
  195. """
  196. 查询所有日程.
  197. """
  198. print("📅 所有日程安排")
  199. print("=" * 50)
  200. events = self.manager.get_events()
  201. if not events:
  202. print("🎉 暂无任何日程安排")
  203. return
  204. print(f"📊 总共有 {len(events)} 个日程:\n")
  205. # 按时间排序并分组显示
  206. now = datetime.now()
  207. past_events = []
  208. current_events = []
  209. future_events = []
  210. for event in events:
  211. start_dt = datetime.fromisoformat(event.start_time)
  212. end_dt = datetime.fromisoformat(event.end_time)
  213. if end_dt < now:
  214. past_events.append(event)
  215. elif start_dt <= now <= end_dt:
  216. current_events.append(event)
  217. else:
  218. future_events.append(event)
  219. # 显示正在进行的事件
  220. if current_events:
  221. print("🔴 正在进行中:")
  222. for event in current_events:
  223. print(f" {self.format_event_display(event, show_details=False)}")
  224. print()
  225. # 显示未来事件
  226. if future_events:
  227. print("⏳ 即将到来:")
  228. for event in future_events[:5]: # 只显示前5个
  229. print(f" {self.format_event_display(event, show_details=False)}")
  230. if len(future_events) > 5:
  231. print(f" ... 还有 {len(future_events) - 5} 个日程")
  232. print()
  233. # 显示最近的过去事件
  234. if past_events:
  235. recent_past = sorted(past_events, key=lambda e: e.start_time, reverse=True)[
  236. :3
  237. ]
  238. print("✅ 最近完成:")
  239. for event in recent_past:
  240. print(f" {self.format_event_display(event, show_details=False)}")
  241. if len(past_events) > 3:
  242. print(f" ... 还有 {len(past_events) - 3} 个已完成的日程")
  243. async def search_events(self, keyword):
  244. """
  245. 搜索日程.
  246. """
  247. print(f"🔍 搜索包含 '{keyword}' 的日程")
  248. print("=" * 50)
  249. all_events = self.manager.get_events()
  250. matched_events = []
  251. for event in all_events:
  252. if (
  253. keyword.lower() in event.title.lower()
  254. or keyword.lower() in event.description.lower()
  255. or keyword.lower() in event.category.lower()
  256. ):
  257. matched_events.append(event)
  258. if not matched_events:
  259. print(f"🎉 没有找到包含 '{keyword}' 的日程")
  260. return
  261. print(f"📊 找到 {len(matched_events)} 个匹配的日程:\n")
  262. for i, event in enumerate(matched_events, 1):
  263. print(f"{i}. {self.format_event_display(event)}")
  264. if i < len(matched_events):
  265. print()
  266. async def main():
  267. """
  268. 主函数.
  269. """
  270. parser = argparse.ArgumentParser(description="日程查询脚本")
  271. parser.add_argument(
  272. "command",
  273. nargs="?",
  274. default="today",
  275. choices=["today", "tomorrow", "week", "upcoming", "category", "all", "search"],
  276. help="查询类型",
  277. )
  278. parser.add_argument("--hours", type=int, default=24, help="upcoming查询的小时数")
  279. parser.add_argument("--category", type=str, help="指定分类名称")
  280. parser.add_argument("--keyword", type=str, help="搜索关键词")
  281. args = parser.parse_args()
  282. script = CalendarQueryScript()
  283. try:
  284. if args.command == "today":
  285. await script.query_today()
  286. elif args.command == "tomorrow":
  287. await script.query_tomorrow()
  288. elif args.command == "week":
  289. await script.query_week()
  290. elif args.command == "upcoming":
  291. await script.query_upcoming(args.hours)
  292. elif args.command == "category":
  293. await script.query_by_category(args.category)
  294. elif args.command == "all":
  295. await script.query_all()
  296. elif args.command == "search":
  297. if not args.keyword:
  298. print("❌ 搜索需要提供关键词,使用 --keyword 参数")
  299. return
  300. await script.search_events(args.keyword)
  301. print("\n" + "=" * 50)
  302. print("💡 使用帮助:")
  303. print(" python scripts/calendar_query.py today # 查看今日日程")
  304. print(" python scripts/calendar_query.py tomorrow # 查看明日日程")
  305. print(" python scripts/calendar_query.py week # 查看本周日程")
  306. print(
  307. " python scripts/calendar_query.py upcoming --hours 48 # 查看未来48小时"
  308. )
  309. print(
  310. " python scripts/calendar_query.py category --category 工作 # 查看工作分类"
  311. )
  312. print(" python scripts/calendar_query.py all # 查看所有日程")
  313. print(" python scripts/calendar_query.py search --keyword 开发 # 搜索日程")
  314. except Exception as e:
  315. logger.error(f"查询日程失败: {e}", exc_info=True)
  316. print(f"❌ 查询失败: {e}")
  317. if __name__ == "__main__":
  318. asyncio.run(main())