"""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()