242 lines
6.5 KiB
Python
242 lines
6.5 KiB
Python
|
|
"""
|
|||
|
|
Super Productivity 数据解析工具
|
|||
|
|
用于从 Super Productivity 导出的 JSON 中提取任务信息
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import json
|
|||
|
|
from datetime import date
|
|||
|
|
from typing import List, Dict, Optional, Tuple
|
|||
|
|
|
|||
|
|
|
|||
|
|
class TaskEntry:
|
|||
|
|
"""任务条目类"""
|
|||
|
|
|
|||
|
|
def __init__(
|
|||
|
|
self,
|
|||
|
|
task_id: str,
|
|||
|
|
title: str,
|
|||
|
|
project_name: str,
|
|||
|
|
subtasks: Optional[List['TaskEntry']] = None,
|
|||
|
|
is_done: bool = False,
|
|||
|
|
time_spent: int = 0
|
|||
|
|
):
|
|||
|
|
self.id = task_id
|
|||
|
|
self.title = title
|
|||
|
|
self.project_name = project_name
|
|||
|
|
self.subtasks = subtasks or []
|
|||
|
|
self.is_done = is_done
|
|||
|
|
self.time_spent = time_spent # 单位:毫秒
|
|||
|
|
|
|||
|
|
def __repr__(self):
|
|||
|
|
return f"TaskEntry(title='{self.title}', project='{self.project_name}', subtasks={len(self.subtasks)})"
|
|||
|
|
|
|||
|
|
def get_time_spent_hours(self) -> float:
|
|||
|
|
"""获取花费的时间(小时)"""
|
|||
|
|
return self.time_spent / 3600000.0
|
|||
|
|
|
|||
|
|
|
|||
|
|
def parse_super_productivity_json(json_file_path: str) -> Tuple[Dict, Dict, Dict]:
|
|||
|
|
"""
|
|||
|
|
解析 Super Productivity JSON 文件
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
json_file_path: JSON 文件路径
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
(tasks, projects, tags) 元组
|
|||
|
|
"""
|
|||
|
|
with open(json_file_path, 'r', encoding='utf-8') as f:
|
|||
|
|
data = json.load(f)
|
|||
|
|
|
|||
|
|
tasks = data['mainModelData']['task']['entities']
|
|||
|
|
projects = data['mainModelData']['project']['entities']
|
|||
|
|
tags = data['mainModelData']['tag']['entities']
|
|||
|
|
|
|||
|
|
return tasks, projects, tags
|
|||
|
|
|
|||
|
|
|
|||
|
|
def get_project_name(projects: Dict, project_id: str) -> str:
|
|||
|
|
"""获取项目名称"""
|
|||
|
|
if project_id in projects:
|
|||
|
|
return projects[project_id]['title']
|
|||
|
|
return "Inbox"
|
|||
|
|
|
|||
|
|
|
|||
|
|
def parse_task_entry(
|
|||
|
|
task_id: str,
|
|||
|
|
tasks: Dict,
|
|||
|
|
projects: Dict
|
|||
|
|
) -> Optional[TaskEntry]:
|
|||
|
|
"""
|
|||
|
|
解析单个任务条目
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
task_id: 任务 ID
|
|||
|
|
tasks: 所有任务字典
|
|||
|
|
projects: 所有项目字典
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
TaskEntry 对象或 None
|
|||
|
|
"""
|
|||
|
|
task = tasks.get(task_id)
|
|||
|
|
if not task:
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
# 获取基本信息
|
|||
|
|
title = task.get('title', 'Untitled')
|
|||
|
|
project_id = task.get('projectId', 'INBOX_PROJECT')
|
|||
|
|
project_name = get_project_name(projects, project_id)
|
|||
|
|
is_done = task.get('isDone', False)
|
|||
|
|
time_spent = task.get('timeSpent', 0)
|
|||
|
|
|
|||
|
|
# 解析子任务
|
|||
|
|
subtask_ids = task.get('subTaskIds', [])
|
|||
|
|
subtasks = []
|
|||
|
|
for subtask_id in subtask_ids:
|
|||
|
|
subtask = parse_task_entry(subtask_id, tasks, projects)
|
|||
|
|
if subtask:
|
|||
|
|
subtasks.append(subtask)
|
|||
|
|
|
|||
|
|
return TaskEntry(
|
|||
|
|
task_id=task_id,
|
|||
|
|
title=title,
|
|||
|
|
project_name=project_name,
|
|||
|
|
subtasks=subtasks,
|
|||
|
|
is_done=is_done,
|
|||
|
|
time_spent=time_spent
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def get_today_tasks(
|
|||
|
|
json_file_path: str,
|
|||
|
|
target_date: Optional[date] = None,
|
|||
|
|
include_done: bool = False
|
|||
|
|
) -> List[TaskEntry]:
|
|||
|
|
"""
|
|||
|
|
获取今天 due 的任务
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
json_file_path: JSON 文件路径
|
|||
|
|
target_date: 目标日期,默认为今天
|
|||
|
|
include_done: 是否包含已完成的任务
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
今天的任务列表
|
|||
|
|
"""
|
|||
|
|
if target_date is None:
|
|||
|
|
target_date = date.today()
|
|||
|
|
|
|||
|
|
today_str = target_date.strftime('%Y-%m-%d')
|
|||
|
|
tasks, projects, _ = parse_super_productivity_json(json_file_path)
|
|||
|
|
|
|||
|
|
today_tasks = []
|
|||
|
|
for task_id, task in tasks.items():
|
|||
|
|
# 检查是否是今天的任务
|
|||
|
|
due_day = task.get('dueDay')
|
|||
|
|
# print(f'Task ID: {task_id}, Due Day: {due_day}, Today: {today_str}') # 调试输出
|
|||
|
|
if due_day != today_str:
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
# 检查是否已完成
|
|||
|
|
if not include_done and task.get('isDone', False):
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
# 只添加顶层任务(没有 parentId 的)
|
|||
|
|
if not task.get('parentId'):
|
|||
|
|
parsed_task = parse_task_entry(task_id, tasks, projects)
|
|||
|
|
if parsed_task:
|
|||
|
|
today_tasks.append(parsed_task)
|
|||
|
|
|
|||
|
|
return today_tasks
|
|||
|
|
|
|||
|
|
|
|||
|
|
def get_tasks_by_tag(
|
|||
|
|
json_file_path: str,
|
|||
|
|
tag_name: str,
|
|||
|
|
include_done: bool = False
|
|||
|
|
) -> List[TaskEntry]:
|
|||
|
|
"""
|
|||
|
|
根据标签获取任务
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
json_file_path: JSON 文件路径
|
|||
|
|
tag_name: 标签名称(如 "TODAY")
|
|||
|
|
include_done: 是否包含已完成的任务
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
带有该标签的任务列表
|
|||
|
|
"""
|
|||
|
|
tasks, projects, tags = parse_super_productivity_json(json_file_path)
|
|||
|
|
|
|||
|
|
# 查找标签 ID
|
|||
|
|
tag_id = None
|
|||
|
|
for tid, tag in tags.items():
|
|||
|
|
if tag.get('title') == tag_name:
|
|||
|
|
tag_id = tid
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
if not tag_id:
|
|||
|
|
return []
|
|||
|
|
|
|||
|
|
# 获取带有该标签的任务
|
|||
|
|
task_ids = tags[tag_id].get('taskIds', [])
|
|||
|
|
result_tasks = []
|
|||
|
|
|
|||
|
|
for task_id in task_ids:
|
|||
|
|
task = tasks.get(task_id)
|
|||
|
|
if not task:
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
# 检查是否已完成
|
|||
|
|
if not include_done and task.get('isDone', False):
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
parsed_task = parse_task_entry(task_id, tasks, projects)
|
|||
|
|
if parsed_task:
|
|||
|
|
result_tasks.append(parsed_task)
|
|||
|
|
|
|||
|
|
return result_tasks
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
"""测试代码"""
|
|||
|
|
import sys
|
|||
|
|
|
|||
|
|
if len(sys.argv) < 2:
|
|||
|
|
print("Usage: python super_productivity_utils.py <json_file_path>")
|
|||
|
|
sys.exit(1)
|
|||
|
|
|
|||
|
|
json_file = sys.argv[1]
|
|||
|
|
|
|||
|
|
# 获取今天的任务
|
|||
|
|
print("=" * 60)
|
|||
|
|
print("📅 TODAY'S TASKS (with due date)")
|
|||
|
|
print("=" * 60)
|
|||
|
|
today_tasks = get_today_tasks(json_file)
|
|||
|
|
|
|||
|
|
for task in today_tasks:
|
|||
|
|
print(f"\n📋 {task.title}")
|
|||
|
|
print(f" 📁 Project: {task.project_name}")
|
|||
|
|
|
|||
|
|
if task.subtasks:
|
|||
|
|
print(f" └─ Subtasks ({len(task.subtasks)}):")
|
|||
|
|
for subtask in task.subtasks:
|
|||
|
|
status = "✓" if subtask.is_done else "○"
|
|||
|
|
print(f" {status} {subtask.title}")
|
|||
|
|
|
|||
|
|
# 获取 TODAY 标签的任务
|
|||
|
|
print("\n" + "=" * 60)
|
|||
|
|
print("🏷️ TASKS WITH 'TODAY' TAG")
|
|||
|
|
print("=" * 60)
|
|||
|
|
tagged_tasks = get_tasks_by_tag(json_file, "TODAY")
|
|||
|
|
|
|||
|
|
for task in tagged_tasks:
|
|||
|
|
print(f"\n📋 {task.title}")
|
|||
|
|
print(f" 📁 Project: {task.project_name}")
|
|||
|
|
if task.time_spent > 0:
|
|||
|
|
print(f" ⏱️ Time spent: {task.get_time_spent_hours():.1f}h")
|
|||
|
|
|
|||
|
|
print("\n" + "=" * 60)
|
|||
|
|
print(f"📊 Summary: {len(today_tasks)} due today, {len(tagged_tasks)} tagged TODAY")
|
|||
|
|
print("=" * 60)
|