import os, json, datetime from dataclasses import dataclass from typing import List, Optional filepath = os.path.expanduser("~/Downloads/super-productivity-backup.json") with open(filepath) as f: data = json.loads(f.read()) @dataclass class Task: id: str parent: Optional[str] title: str reminder: Optional[int] planned: Optional[int] tags: List[str] children: List[str] done: bool # reminder id to timestamp mapping reminders = { reminder["id"]: reminder["remindAt"] for reminder in data["reminders"] } # tag id to tag name mapping tags = { id: tag["title"] for id, tag in data["tag"]["entities"].items() } projects = { id: project["taskIds"] for id, project in data["project"]["entities"].items() } # task id to task object mapping tasks = { id: Task( id = id, parent = task["parentId"], title = task["title"], reminder = reminders[task["reminderId"]] if task["reminderId"] else None, planned = task["plannedAt"], tags = task["tagIds"], children = task["subTaskIds"], done = task["isDone"] ) for id, task in data["task"]["entities"].items() } # convert a task to org-mode according to its level def process_task(task: Task, level: int): asterisks = "*" * level tags_str = ":".join([tags[tag_id] for tag_id in task.tags]) if tags_str: tags_str = f":{tags_str}:" status = "DONE" if task.done else "TODO" # print headline in a children aware way if task.children: total = len(task.children) done = sum(1 if task.done else 0 for task in (tasks[id] for id in task.children)) print(f"{asterisks} {status} [{done}/{total}] {task.title} {tags_str}") else: print(f"{asterisks} {status} {task.title} {tags_str}") # print scheduled and deadline (if any) if task.planned: print(f"SCHEDULED: {timestamp_to_org(task.planned / 1000)} ", end="") if task.reminder: print(f"DEADLINE: {timestamp_to_org(task.reminder / 1000)}") print("") # now recursively handle all sub-tasks for child in task.children: process_task(tasks[child], level = level + 1) def timestamp_to_org(ts: int) -> str: return datetime.datetime.fromtimestamp(ts).strftime("<%Y-%m-%d %a %H:%M>") def main(): # We start with iterating top-level tasks that have no parent for task in tasks.values(): if task.parent == None: process_task(task, level = 1) # However if you prefer to start from Projects as top level headline instead, # Then comment out above part, and uncomment the below # for project, task_ids in projects.items(): # print(f"* {project}\n") # for id in task_ids: # process_task(tasks[id], level = 2) main()