Skip to content

Instantly share code, notes, and snippets.

@ErikKalkoken
Created June 14, 2023 19:28
Show Gist options
  • Save ErikKalkoken/25c3ec2319316d16e9c72749f6158807 to your computer and use it in GitHub Desktop.
Save ErikKalkoken/25c3ec2319316d16e9c72749f6158807 to your computer and use it in GitHub Desktop.
Convert iphone notes from JSON format into plain text files.
"""Convert iphone notes from JSON format into plain text files."""
import argparse
import datetime as dt
import json
import os
import re
from pathlib import Path
from typing import NamedTuple
def get_valid_filename(name):
"""Transform a string into a valid filename and return it."""
s = str(name).strip().replace(" ", "_")
s = re.sub(r"(?u)[^-\w.]", "", s)
s = s.replace("_", " ")
if s in {"", ".", ".."}:
raise ValueError("Could not derive file name from '%s'" % name)
return s
class Note(NamedTuple):
"""An extracted iphone note."""
title: str
text: str
created: dt.datetime
last_modified: dt.datetime
@classmethod
def from_obj(cls, obj) -> "Note":
return cls(
title=obj["title"],
text=obj["plaintext"],
created=cls._parse_time(obj["creation_time"]),
last_modified=cls._parse_time(obj["modify_time"]),
)
@staticmethod
def _parse_time(time_string: str) -> dt.datetime:
return dt.datetime.strptime(time_string, "%Y-%m-%d %H:%M:%S %z")
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"filename", help="Name of the input JSON file, e.g. all_notes.json"
)
args = parser.parse_args()
# fetch notes from file
input_file = Path(args.filename)
with input_file.open("r", encoding="utf-8") as file:
data = json.load(file)
# extract notes
notes = [Note.from_obj(obj) for obj in data["notes"].values()]
# write notes as plain text files
output_folder = Path(__file__).parent / "output"
output_folder.mkdir(exist_ok=True)
for note in notes:
filename = get_valid_filename(note.title)
output_file = output_folder / f"{filename}.txt"
with output_file.open("w", encoding="utf-8") as file:
file.write(note.text)
os.utime(
output_file, (note.created.timestamp(), note.last_modified.timestamp())
)
print(f"Converted {len(notes)} notes into plain text files at: {output_folder}")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment