diff --git a/.gitignore b/.gitignore index 054c593..fa2f50a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,97 @@ -.DS_Store -*.tmp -*.log -tmp/ -log/ -venv/ -.venv/ +# Specific files cfg/filters *.secret db/ + +# Os generated files +## Linux +.Trash-* + +## MacOS +.DS_Store +.AppleDouble +.LSOverride +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk +._* + +## Windows +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db +Desktop.ini +desktop.ini +$RECYCLE.BIN/ + +# temporary or log(s) files and directories +*.tmp +*.log +*.logs +tmp/ +log/ +logs/ + +# Python +venv/ +.venv/ +__pycache__/ + +# LaTeX +Build/ +build/ +*.acn +*.acr +*.alg +*.aux +*.bak +*.bbl +*.bcf +*.blg +*.brf +*.bst +*.dvi +*.fdb_latexmk +*.fls +*.glg +*.glo +*.gls +*.idx +*.ilg +*.ind +*.ist +*.lof +*.log +*.lol +*.lot +*.maf +*.mtc +*.mtc1 +*.nav +*.nlo +*.nls +*.out +*.pyg +*.run.xml +*.snm +*.synctex.gz +*.tex.backup +*.tex~ +*.thm +*.toc +*.vrb +*.xdy +*.xml +*blx.bib +.bak +.mtc diff --git a/setup.sh b/setup.sh index a39416b..5720c9b 100755 --- a/setup.sh +++ b/setup.sh @@ -32,7 +32,7 @@ mkdir -p -v cfg/filters mkdir -p -v db touch db/read.bib touch db/unread.bib -touch db/library.bib +touch db/local.bib chmod u+x *.sh echo "=== Done ===" diff --git a/src/__pycache__/ads_api.cpython-312.pyc b/src/__pycache__/ads_api.cpython-312.pyc deleted file mode 100644 index 31e03aa..0000000 Binary files a/src/__pycache__/ads_api.cpython-312.pyc and /dev/null differ diff --git a/src/__pycache__/arxiv_api.cpython-312.pyc b/src/__pycache__/arxiv_api.cpython-312.pyc deleted file mode 100644 index 13b0891..0000000 Binary files a/src/__pycache__/arxiv_api.cpython-312.pyc and /dev/null differ diff --git a/src/__pycache__/bibtex_interface.cpython-312.pyc b/src/__pycache__/bibtex_interface.cpython-312.pyc deleted file mode 100644 index 85bbabe..0000000 Binary files a/src/__pycache__/bibtex_interface.cpython-312.pyc and /dev/null differ diff --git a/src/__pycache__/local_api.cpython-312.pyc b/src/__pycache__/local_api.cpython-312.pyc deleted file mode 100644 index fe30ffd..0000000 Binary files a/src/__pycache__/local_api.cpython-312.pyc and /dev/null differ diff --git a/src/__pycache__/utils.cpython-312.pyc b/src/__pycache__/utils.cpython-312.pyc deleted file mode 100644 index 79a6144..0000000 Binary files a/src/__pycache__/utils.cpython-312.pyc and /dev/null differ diff --git a/src/ads_api.py b/src/ads_api.py index 3028e20..58eb988 100644 --- a/src/ads_api.py +++ b/src/ads_api.py @@ -93,8 +93,8 @@ def ads_bibcode(bibcodes): bibentry = feed.text bibentry = bibentry[:-2] bibentry += (",\n" - "\tarxtic_notes={},\n" - "\tarxtic_category={},\n" + "\tarxtic_comment={},\n" + "\tarxtic_library={},\n" "\tarxtic_keywords={},\n" "\tarxtic_score={-1},\n" "\tarxtic_filename={},\n" diff --git a/src/arxiv_api.py b/src/arxiv_api.py index dd424f6..5b27063 100644 --- a/src/arxiv_api.py +++ b/src/arxiv_api.py @@ -102,8 +102,8 @@ def parse_bibtex(entries, f"\teprint={{{eprint}}},\n" f"\turl={{{url}}},\n" f"\tabstract={{{abstract}}},\n" - "\tarxtic_notes={},\n" - "\tarxtic_category={},\n" + "\tarxtic_comment={},\n" + "\tarxtic_library={},\n" "\tarxtic_keywords={},\n" "\tarxtic_score={-1},\n" "\tarxtic_filename={},\n" diff --git a/src/arxtic.py b/src/arxtic.py index 554f012..ec5a7b2 100644 --- a/src/arxtic.py +++ b/src/arxtic.py @@ -31,6 +31,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see www.gnu.org/licenses/. """ import os +import subprocess from urllib.parse import urlencode, quote_plus import requests as rq @@ -59,9 +60,138 @@ ids = ["2510.06329", "2509.13163"] bibcodes = ["2022A&A...658A.152V", "2021A&A...649A..97L"] query = "first_author:\"Voggel, K\"year:(2022)" -utils.print_reference(arxiv_api.arxiv_today()) -utils.print_title_author(arxiv_api.arxiv_id(ids)) -print(ads_api.ads_bibcode_search(query, num=2)) -utils.print_abstract(ads_api.ads_bibcode(bibcodes)) +#test1 = local_api.update_local_pdf() +#local_api.bibtex_to_file(test1, "test1") +#test1 = local_api.file_to_bibtex("test1") +#test2 = bib.Library() +# +#utils.arxtic_comment("2022ApJ...941L..11F", test1) +#utils.arxtic_library_add("2022ApJ...941L..11F", test1, "test1") +#utils.arxtic_keywords_add("2022ApJ...941L..11F", test1, "kw1") +#utils.arxtic_score("2022ApJ...941L..11F", test1, "42") +#utils.arxtic_date_read("2022ApJ...941L..11F", test1) +#utils.export_abstract(test1, "test1") + +if __name__ == "__main__": + q = False + s = True + libraries_names = [f.replace(".bib", "") for f in os.listdir(DB_DIR) + if not f[0] == "." and ".bib" in f] + libraries = {l: local_api.file_to_bibtex(l + ".bib") + for l in libraries_names} + while not q: + print(COLOUR_INPUT + "Select an action") + val = input("> " + COLOUR_DEFAULT) + args = val.split(" ") + nargs = len(args) + + arg0 = args[0].lower() + + try: + + if arg0 in ["quit", "exit", "q"]: + if s == True: + q = True + else: + raise Exception(("Unsaved changes, " + "type \"save\" to update database or " + "type \"cancel\" to discard")) + + elif arg0 in ["save", "write"]: + for i in range(len(libraries)): + lib_name = libraries_names[i] + lib = libraries[lib_name] + local_api.bibtex_to_file(lib, lib_name) + s = True + + elif arg0 in ["cancel", "reset", "reload"]: + for i in range(len(libraries)): + lib_name = libraries_names[i] + libraries[lib_name] = local_api.file_to_bibtex(lib_name) + s = True + + elif arg0 in ["local"]: + libraries["local"] = local_api.update_local_pdf( + library=libraries["local"]) + + elif arg0 in ["print"]: + if nargs == 2: + utils.print_abstract(libraries[args[1]]) + elif nargs == 3: + if args[2] in ["abstract", "abs"]: + utils.print_abstract(libraries[args[1]]) + if args[2] in ["title", "author", "authors"]: + utils.print_title_author(libraries[args[1]]) + if args[2] in ["reference", "references", "ref", "refs"]: + utils.print_reference(libraries[args[1]]) + else: + raise Exception("Not recognized") + + elif arg0 in ["export"]: + if nargs == 1: + for i in range(len(libraries)): + lib_name = libraries_names[i] + lib = libraries[lib_name] + utils.export_abstract(lib, lib_name+".txt") + + elif nargs == 2: + utils.export_abstract(libraries[args[1]], args[1]+".txt") + elif nargs == 3: + if args[2] in ["abstract", "abs"]: + utils.export_abstract(libraries[args[1]], args[1]+".txt") + if args[2] in ["title", "author", "authors"]: + utils.export_title_author(libraries[args[1]], args[1]+".txt") + if args[2] in ["reference", "references", "ref", "refs"]: + utils.export_reference(libraries[args[1]], args[1]+".txt") + else: + raise Exception("Not recognized") + + elif arg0 in ["mv", "move"]: + if nargs == 4: + utils.mv(args[1], libraries[args[2]], libraries[args[3]]) + s = False + else: + raise Exception("Not recognized") + + elif arg0 in ["cp", "copy"]: + if nargs == 4: + utils.cp(args[1], libraries[args[2]], libraries[args[3]]) + s = False + else: + raise Exception("Not recognized") + + elif arg0 in ["rm", "remove", "del", "delete"]: + if nargs == 3: + utils.rm(args[1], libraries[args[2]]) + s = False + else: + raise Exception("Not recognized") + + elif arg0 in ["comment"]: + if nargs == 3: + utils.arxtic_comment(args[2], libraries[args[2]]) + s = False + + elif arg0 in ["read"]: + if nargs == 2: + utils.launch_reader(args[1], libraries["local"]) + utils.arxtic_comment(args[1], libraries["local"]) + s = False + elif nargs == 3: + utils.launch_reader(args[1], libraries[args[2]]) + utils.arxtic_comment(args[1], libraries[args[2]]) + s = False + else: + raise Exception("Not recognized") + + else: + raise Exception("Not recognized") + except Exception as err: + msg = str(err) + print(COLOUR_ERROR + "Error! " + msg + COLOUR_DEFAULT) + + + + + -utils.print_reference(local_api.update_local_pdf()) diff --git a/src/local_api.py b/src/local_api.py index 030ce10..9563425 100644 --- a/src/local_api.py +++ b/src/local_api.py @@ -76,7 +76,7 @@ def update_local_pdf(library=None, directory=PDF_DIR): known_pdf = [block["arxtic_filename"] for block in blocks] folder_pdf = [f for f in os.listdir(directory) - if not f[0] == "." and ".pdf" if f] + if not f[0] == "." and ".pdf" in f] for pdf in folder_pdf: fields = pdf.replace(".pdf", "").split("_") @@ -92,6 +92,7 @@ def update_local_pdf(library=None, directory=PDF_DIR): arxiv_id = "/".join(fields[2:]) arxiv_library = arxiv_api.arxiv_id(arxiv_id) if len(arxiv_library.blocks) == 1: + arxiv_library.blocks[0]["arxtic_filename"] = pdf library.add(arxiv_library.blocks) else: print(COLOUR_WARNING @@ -113,6 +114,7 @@ def update_local_pdf(library=None, directory=PDF_DIR): bibcodes = ads_api.ads_bibcode_search(query, num=2) if len(bibcodes) == 1: ads_library = ads_api.ads_bibcode(bibcodes) + ads_library.blocks[0]["arxtic_filename"] = pdf library.add(ads_library.blocks) else: print(COLOUR_WARNING diff --git a/src/utils.py b/src/utils.py index 3fb7576..9a72d50 100644 --- a/src/utils.py +++ b/src/utils.py @@ -31,6 +31,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see www.gnu.org/licenses/. """ import os +import subprocess from urllib.parse import urlencode, quote_plus import requests as rq @@ -38,39 +39,89 @@ import numpy as np import textwrap as tw import feedparser as fp import bibtexparser as bib +import datetime as dt FILTERS_DIR = os.environ.get("FILTERS_DIR") DB_DIR = os.environ.get("DB_DIR") PDF_DIR = os.environ.get("PDF_DIR") -COLOUR_DEFAULT="\033[0m" -COLOUR_INPUT="\033[36m" -COLOUR_OUTPUT="\033[32m" -COLOUR_INFO="\033[34m" -COLOUR_WARNING="\033[93m" -COLOUR_ERROR="\033[91m" +COLOUR_DEFAULT = "\033[0m" +COLOUR_INPUT = "\033[36m" +COLOUR_OUTPUT = "\033[32m" +COLOUR_INFO = "\033[34m" +COLOUR_WARNING = "\033[93m" +COLOUR_ERROR = "\033[91m" + +COLOUR_REF = "\033[34m" +COLOUR_TITLE = "\033[35m" +COLOUR_AUTHOR = "\033[32m" +COLOUR_ABS = "\033[36m" +COLOUR_ARXTIC = "\033[91m" + + +## General def wrap(txt, length=80): wrapped_txt = '\n'.join(tw.wrap(txt, length, break_long_words=False)) return wrapped_txt +## Prints + def print_abstract(library): if not isinstance(library, bib.Library): library = bib.Library(library) for block in library.blocks: if isinstance(block, bib.model.Block): - print(COLOUR_INFO, end="") - print(block.key, end="") - print(" [" + block["url"] + "]", end="") - print(COLOUR_DEFAULT) - - print(COLOUR_DEFAULT + wrap(block["title"]) + COLOUR_DEFAULT) - print(COLOUR_OUTPUT + print("#" + + COLOUR_REF + + block.key + " [" + block["url"] + "]" + + COLOUR_DEFAULT) + if block["arxtic_filename"] != "": + print(COLOUR_REF + + "(" + + PDF_DIR + + block["arxtic_filename"] + + ")" + + COLOUR_DEFAULT) + print("Title: " + + COLOUR_TITLE + + wrap(block["title"]) + + COLOUR_DEFAULT) + print("Author(s): " + + COLOUR_AUTHOR + wrap(", ".join(block["author"].split(" and "))) + COLOUR_DEFAULT) - print(COLOUR_INPUT + print("Abstract: " + + COLOUR_ABS + wrap(block["abstract"]) + COLOUR_DEFAULT) + if block["arxtic_comment"] != "": + print("Comment: " + + COLOUR_ARXTIC + + wrap(block["arxtic_comment"]) + + COLOUR_DEFAULT) + if block["arxtic_library"] != "": + print("Library: " + + COLOUR_ARXTIC + + ", ".join(block["arxtic_library"].split(",")) + + COLOUR_DEFAULT) + if block["arxtic_keywords"] != "": + print("Keywords: " + + COLOUR_ARXTIC + + ", ".join(block["arxtic_library"].split(",")) + + COLOUR_DEFAULT) + if float(block["arxtic_score"]) >= 0: + print("Score: " + + COLOUR_ARXTIC + + block["arxtic_score"] + + COLOUR_DEFAULT) + if block["arxtic_date_read"] != "": + print("Read date: " + + COLOUR_ARXTIC + + block["arxtic_date_read"] + + COLOUR_DEFAULT) + print("") + print(80*"=") print("") return 0 @@ -79,15 +130,52 @@ def print_title_author(library): library = bib.Library(library) for block in library.blocks: if isinstance(block, bib.model.Block): - print(COLOUR_INFO, end="") - print(block.key, end="") - print(" [" + block["url"] + "]", end="") - print(COLOUR_DEFAULT) - - print(COLOUR_DEFAULT + wrap(block["title"]) + COLOUR_DEFAULT) - print(COLOUR_OUTPUT + print("#" + + COLOUR_REF + + block.key + " [" + block["url"] + "]" + + COLOUR_DEFAULT) + if block["arxtic_filename"] != "": + print(COLOUR_REF + + "(" + + PDF_DIR + + block["arxtic_filename"] + + ")" + + COLOUR_DEFAULT) + print("Title: " + + COLOUR_TITLE + + wrap(block["title"]) + + COLOUR_DEFAULT) + print("Author(s): " + + COLOUR_AUTHOR + wrap(", ".join(block["author"].split(" and "))) + COLOUR_DEFAULT) + if block["arxtic_comment"] != "": + print("Comment: " + + COLOUR_ARXTIC + + wrap(block["arxtic_comment"]) + + COLOUR_DEFAULT) + if block["arxtic_library"] != "": + print("Library: " + + COLOUR_ARXTIC + + ", ".join(block["arxtic_library"].split(",")) + + COLOUR_DEFAULT) + if block["arxtic_keywords"] != "": + print("Keywords: " + + COLOUR_ARXTIC + + ", ".join(block["arxtic_library"].split(",")) + + COLOUR_DEFAULT) + if float(block["arxtic_score"]) >= 0: + print("Score: " + + COLOUR_ARXTIC + + block["arxtic_score"] + + COLOUR_DEFAULT) + if block["arxtic_date_read"] != "": + print("Read date: " + + COLOUR_ARXTIC + + block["arxtic_date_read"] + + COLOUR_DEFAULT) + print("") + print(80*"=") print("") return 0 @@ -101,3 +189,180 @@ def print_reference(library): print(" [" + block["url"] + "]", end="") print(COLOUR_DEFAULT) return 0 + +## Exports + +def export_abstract(library, filename, directory=PDF_DIR): + if not isinstance(library, bib.Library): + library = bib.Library(library) + with open(directory + filename, "w+") as file: + for block in library.blocks: + if isinstance(block, bib.model.Block): + file.write("#" + block.key + " [" + block["url"] + "]\n") + file.write(wrap("Title: " + block["title"]) + "\n") + file.write(wrap("Author(s): " + + ", ".join(block["author"].split(" and "))) + + "\n") + file.write(wrap("Abstract: " + block["abstract"]) + "\n") + if block["arxtic_comment"] != "": + file.write(wrap("Comment: " + block["arxtic_comment"]) + + "\n") + if block["arxtic_library"] != "": + file.write("Library: " + + ", ".join(block["arxtic_library"].split(",")) + + "\n") + if block["arxtic_keywords"] != "": + file.write("Keywords: " + + ", ".join(block["arxtic_library"].split(",")) + + "\n") + if float(block["arxtic_score"]) >= 0: + file.write("Score: " + block["arxtic_score"] + "\n") + if block["arxtic_date_read"] != "": + file.write("Read date: " + block["arxtic_date_read"] + "\n") + file.write("\n" + 80*"=" + "\n") + file.write("\n") + return 0 + +def export_title_author(library, filename, directory=PDF_DIR): + if not isinstance(library, bib.Library): + library = bib.Library(library) + with open(directory + filename, "w+") as file: + for block in library.blocks: + if isinstance(block, bib.model.Block): + file.write("#" + block.key + " [" + block["url"] + "]\n") + file.write(wrap("Title: " + block["title"]) + "\n") + file.write(wrap("Author(s): " + + ", ".join(block["author"].split(" and "))) + + "\n") + if block["arxtic_comment"] != "": + file.write(wrap("Comment: " + block["arxtic_comment"]) + + "\n") + if block["arxtic_library"] != "": + file.write("Library: " + + ", ".join(block["arxtic_library"].split(",")) + + "\n") + if block["arxtic_keywords"] != "": + file.write("Keywords: " + + ", ".join(block["arxtic_library"].split(",")) + + "\n") + if float(block["arxtic_score"]) >= 0: + file.write("Score: " + block["arxtic_score"] + "\n") + if block["arxtic_date_read"] != "": + file.write("Read date: " + block["arxtic_date_read"] + "\n") + file.write("\n" + 80*"=" + "\n") + file.write("\n") + return 0 + +def export_reference(library, filename, directory=PDF_DIR): + if not isinstance(library, bib.Library): + library = bib.Library(library) + with open(directory + filename, "w+") as file: + for block in library.blocks: + if isinstance(block, bib.model.Block): + file.write("#" + block.key + " [" + block["url"] + "]\n") + return 0 + +## Manipulation + +def find(key, library): + blocks = library.blocks + i = 0 + while i < len(blocks): + block = blocks[i] + if isinstance(block, bib.model.Block): + if block.key == key: + return block + i += 1 + return None + +def mv(key, lib_from, lib_to): + block = find(key, lib_from) + lib_to.add(block) + lib_from.remove(block) + return lib_from, lib_to + +def cp(key, lib_from, lib_to): + block = find(key, lib_from) + lib_to.add(block) + return lib_from, lib_to + +def rm(key, library): + block = find(key, library) + library.remove(block) + return library + +## ArXtic parameters + +def arxtic_comment(key, library): + block = find(key, library) + comment = block["arxtic_comment"] + with open("comment.tmp", "w+") as file: + file.write(comment) + subprocess.run(["nvim", "comment.tmp"]) + with open("comment.tmp", "r") as file: + comment = "".join(file.readlines()) + subprocess.run(["rm", "comment.tmp"]) + block["arxtic_comment"] = comment + return library + +def arxtic_library_add(key, library, value): + block = find(key, library) + old_value = block["arxtic_library"] + new_value = old_value + "," + value + if new_value[0] == ",": new_value = new_value[1:] + if new_value[-1] == ",": new_value = new_value[:-1] + block["arxtic_library"] = new_value + return library + +def arxtic_library_remove(key, library, value): + block = find(key, library) + old_value = block["arxtic_library"] + new_value = old_value.replace(value, "").replace(",,", ",") + if new_value[0] == ",": new_value = new_value[1:] + if new_value[-1] == ",": new_value = new_value[:-1] + block["arxtic_library"] = new_value + return library + +def arxtic_keywords_add(key, library, value): + block = find(key, library) + old_value = block["arxtic_keywords"] + new_value = old_value + "," + value + if new_value[0] == ",": new_value = new_value[1:] + if new_value[-1] == ",": new_value = new_value[:-1] + block["arxtic_keywords"] = new_value + return library + +def arxtic_keywords_remove(key, library, value): + block = find(key, library) + old_value = block["arxtic_keywords"] + new_value = old_value.replace(value, "").replace(",,", ",") + if new_value[0] == ",": new_value = new_value[1:] + if new_value[-1] == ",": new_value = new_value[:-1] + block["arxtic_keywords"] = new_value + return library + +def arxtic_score(key, library, score): + block = find(key, library) + block["arxtic_score"] = score + return library + +def arxtic_date_read(key, library, date=None): + block = find(key, library) + if date is None: + date = dt.datetime.now() + block["arxtic_date_read"] = date.strftime("%Y-%m-%d") + return library + +def arxtic_filename(key, library, filename): + block = find(key, library) + block["arxtic_filename"] = filename + return library + +## External reader + +def launch_reader(key, library, directory=PDF_DIR): + block = find(key, library) + fname = block["arxtic_filename"] + subprocess.Popen(["/bin/flatpak", "run", "com.github.ahrm.sioyek", directory+fname], + stdout = subprocess.DEVNULL, + stderr = subprocess.DEVNULL)