From a7a8ebf8ed0b326d859aa624eb31cdd018fd1457 Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Tue, 29 Aug 2023 22:44:33 +0200 Subject: [PATCH] papis: Add full code for papis-marvin plugin --- writing/.config/papis/scripts/papis-marvin | 164 ++++++++++++++++++++- 1 file changed, 163 insertions(+), 1 deletion(-) mode change 120000 => 100755 writing/.config/papis/scripts/papis-marvin diff --git a/writing/.config/papis/scripts/papis-marvin b/writing/.config/papis/scripts/papis-marvin deleted file mode 120000 index 2bc2fad..0000000 --- a/writing/.config/papis/scripts/papis-marvin +++ /dev/null @@ -1 +0,0 @@ -/home/marty/projects/python/papis/marvin/papis-marvin \ No newline at end of file diff --git a/writing/.config/papis/scripts/papis-marvin b/writing/.config/papis/scripts/papis-marvin new file mode 100755 index 0000000..479a4da --- /dev/null +++ b/writing/.config/papis/scripts/papis-marvin @@ -0,0 +1,163 @@ +#!/usr/bin/env python +# papis-short-help: Import iOS Marvin exported csv annotations +# +# This script can be used to import your highlights and notes from +# the iOS application 'Marvin Reader'. In the app, export your +# annotations as 'csv' format and then point the script to the +# resulting file. +# https://git.martyoeh.me/Marty/papis-marvin +import os +import sys +import re +import logging +from typing import Dict +import papis.api +import papis.pick +import papis.format +import papis.commands.edit +import papis.commands.list +import papis.commands.add +import papis.notes +import papis.config +import papis.database +import isbnlib +import papis.isbn + +logger = logging.getLogger("marvin") +logger.setLevel(logging.DEBUG) + +DEFAULT_CSV_PATH = "/home/marty/Nextcloud/Personal/Backups/Journal.csv" + + +def main(fpath, db): + with open(fpath) as f: + import csv + + csv = csv.DictReader(f) + notes = get_all_annotations(db, csv) + + write_to_files(notes) + + +def get_all_annotations(db, csv) -> Dict: + notes = {} + note_file = "" + for row in csv: + # switch to next book + if not is_same_book(row["Title"]): + doc = get_document(db, row["Author"], row["Title"]) + if not doc: + continue + note_file = get_notefile(db, doc) + + text = format_entry(row) + + if note_file and text: + if note_file not in notes.keys(): + notes[note_file] = [] + notes[note_file].append(text) + return notes + + +def get_document(db, author, title): + res = query_document(db, author, title) + if not res: + add_to_database(author, title) + res = query_document(db, author, title) + if not res: + logger.warning(f"Nothing found for {author}: {title}.\nPlease create manually.") + return + return res + + +# TODO warn user/ let him pick with picker if multiple docs found +def query_document(db, author, title): + title = strip_string(title) + for query in [f"author:({author}) title:({title})"]: + print(f"query: {query}") + res = db.query(query) + if len(res) >= 1: + return res[0] + + +def add_to_database(author, title, confirm=True, edit=False): + logger.info(f"Searching - '{title} {author}'") + data = None + try: + data = papis.isbn.get_data(f"{title}") + except isbnlib.ISBNLibException as e: + logger.error(e) + else: + logger.warning(f"Found: {data}") + if data: + papis_data = papis.isbn.data_to_papis(data[0]) + papis.commands.add.run([], data=papis_data, confirm=confirm, edit=edit) + + +def get_notefile(db, document) -> str | None: + if not document.has("notes"): + notes_name = papis.config.getstring("notes-name") + document["notes"] = papis.format.format(notes_name, document) + document.save() + db.update(document) + + notes_path = os.path.join(str(document.get_main_folder()), document["notes"]) + + if not os.path.exists(notes_path): + # TODO reimplement logger: logger.debug("Creating '%s'", notes_path) + papis.notes.notes_path_ensured(document) + return notes_path + + +# TODO implement custom formatting (akin to pubs-extract) +def format_entry(row) -> str: + text = f"> {row['HighlightText']}" + if row["EntryText"]: + if text: + text += "\n" + else: + text = "> " + text += f"{row['EntryText']}" + return text + + +_old_title = "" + + +def is_same_book(title): + global _old_title + + same = _old_title == title + _old_title = title + if same: + return True + return False + + +def write_to_files(notes: Dict): + # write to notes + for f, entries in notes.items(): + if f: + with open(f, "a") as note: + logger.info(f"Editing {f}...") + num_added = 0 + for entry in entries: + with open(f) as noteread: + if entry not in noteread.read(): + note.write(f"{entry}\n\n") + num_added += 1 + logger.info(f"Added {num_added} entries to it.") + + +strip_pattern = re.compile(r"([^\s\w]|_)+\w*") + + +def strip_string(title) -> str: + return strip_pattern.sub("", title) + + +if __name__ == "__main__": + # use argument passed to command as file or default file here + fpath = sys.argv[1] if len(sys.argv) > 1 else DEFAULT_CSV_PATH + + main(fpath, papis.database.get())