Files
Inkycal/inkycal/modules/super_productivity_utils.py
2025-11-30 21:17:02 +01:00

242 lines
6.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
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)