Snippet content copied to clipboard.
Are you sure to delete this snippet? No, don't delete
  1. import os, json, datetime
  2. from dataclasses import dataclass
  3. from typing import List, Optional
  4. filepath = os.path.expanduser("~/Downloads/super-productivity-backup.json")
  5. with open(filepath) as f:
  6. data = json.loads(f.read())
  7. @dataclass
  8. class Task:
  9. id: str
  10. parent: Optional[str]
  11. title: str
  12. reminder: Optional[int]
  13. planned: Optional[int]
  14. tags: List[str]
  15. children: List[str]
  16. done: bool
  17. # reminder id to timestamp mapping
  18. reminders = {
  19. reminder["id"]: reminder["remindAt"] for reminder in data["reminders"]
  20. }
  21. # tag id to tag name mapping
  22. tags = {
  23. id: tag["title"] for id, tag in data["tag"]["entities"].items()
  24. }
  25. projects = {
  26. id: project["taskIds"] for id, project in data["project"]["entities"].items()
  27. }
  28. # task id to task object mapping
  29. tasks = {
  30. id: Task(
  31. id = id,
  32. parent = task["parentId"],
  33. title = task["title"],
  34. reminder = reminders[task["reminderId"]] if task["reminderId"] else None,
  35. planned = task["plannedAt"],
  36. tags = task["tagIds"],
  37. children = task["subTaskIds"],
  38. done = task["isDone"]
  39. ) for id, task in data["task"]["entities"].items()
  40. }
  41. # convert a task to org-mode according to its level
  42. def process_task(task: Task, level: int):
  43. asterisks = "*" * level
  44. tags_str = ":".join([tags[tag_id] for tag_id in task.tags])
  45. if tags_str:
  46. tags_str = f":{tags_str}:"
  47. status = "DONE" if task.done else "TODO"
  48. # print headline in a children aware way
  49. if task.children:
  50. total = len(task.children)
  51. done = sum(1 if task.done else 0 for task in (tasks[id] for id in task.children))
  52. print(f"{asterisks} {status} [{done}/{total}] {task.title} {tags_str}")
  53. else:
  54. print(f"{asterisks} {status} {task.title} {tags_str}")
  55. # print scheduled and deadline (if any)
  56. if task.planned:
  57. print(f"SCHEDULED: {timestamp_to_org(task.planned / 1000)} ", end="")
  58. if task.reminder:
  59. print(f"DEADLINE: {timestamp_to_org(task.reminder / 1000)}")
  60. print("")
  61. # now recursively handle all sub-tasks
  62. for child in task.children:
  63. process_task(tasks[child], level = level + 1)
  64. def timestamp_to_org(ts: int) -> str:
  65. return datetime.datetime.fromtimestamp(ts).strftime("<%Y-%m-%d %a %H:%M>")
  66. def main():
  67. # We start with iterating top-level tasks that have no parent
  68. for task in tasks.values():
  69. if task.parent == None:
  70. process_task(task, level = 1)
  71. # However if you prefer to start from Projects as top level headline instead,
  72. # Then comment out above part, and uncomment the below
  73. # for project, task_ids in projects.items():
  74. # print(f"* {project}\n")
  75. # for id in task_ids:
  76. # process_task(tasks[id], level = 2)
  77. main()

Edit this Snippet