From e7fbdad02210c88b772346e84ab614f02f24ec03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moussouni=2C=20Ya=C3=ABl?= Date: Wed, 22 Oct 2025 15:39:21 +0200 Subject: [PATCH] 2025-10-22: Automatic update --- src/arxtic.py | 86 +++++++++++++++------ src/legacy.py | 194 ----------------------------------------------- src/local_api.py | 11 +++ src/utils.py | 128 ++++++++++++++++++++++++++++--- 4 files changed, 192 insertions(+), 227 deletions(-) delete mode 100644 src/legacy.py diff --git a/src/arxtic.py b/src/arxtic.py index 5f20a9d..f571e4c 100644 --- a/src/arxtic.py +++ b/src/arxtic.py @@ -56,22 +56,6 @@ COLOUR_INFO="\033[34m" COLOUR_WARNING="\033[93m" COLOUR_ERROR="\033[91m" -ids = ["2510.06329", "2509.13163"] -bibcodes = ["2022A&A...658A.152V", "2021A&A...649A..97L"] -query = "first_author:\"Voggel, K\"year:(2022)" - -#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 @@ -79,6 +63,10 @@ if __name__ == "__main__": if not f[0] == "." and ".bib" in f] libraries = {l: local_api.file_to_bibtex(l + ".bib") for l in libraries_names} + + libraries["local"] = local_api.update_local_pdf(library=libraries["local"]) + libraries["read"] = local_api.update_library(libraries["local"], "read") + libraries["unread"] = local_api.update_library(libraries["local"], "unread") while not q: print(COLOUR_INPUT + "Select an action") val = input("> " + COLOUR_DEFAULT) @@ -104,6 +92,10 @@ if __name__ == "__main__": lib_name = libraries_names[i] lib = libraries[lib_name] local_api.bibtex_to_file(lib, lib_name) + for i in range(len(libraries)): + lib_name = libraries_names[i] + lib = libraries[lib_name] + utils.export_author_year(lib, lib_name+".txt") s = True elif arg0 in ["cancel", "reset", "reload"]: @@ -117,6 +109,13 @@ if __name__ == "__main__": library=libraries["local"]) s = False + elif arg0 in ["update"]: + libraries["local"] = local_api.update_local_pdf( + library=libraries["local"]) + libraries["read"] = local_api.update_library(libraries["local"], "read") + libraries["unread"] = local_api.update_library(libraries["local"], "unread") + s = False + elif arg0 in ["today"]: today_lib = arxiv_api.arxiv_today() libraries_names.append("today") @@ -129,10 +128,13 @@ if __name__ == "__main__": elif nargs == 3: if args[2] in ["abstract", "abs"]: utils.print_abstract(libraries[args[1]]) - if args[2] in ["title", "author", "authors"]: + elif args[2] in ["title"]: utils.print_title_author(libraries[args[1]]) - if args[2] in ["reference", "references", "ref", "refs"]: + elif args[2] in ["reference", "references", "ref", "refs"]: utils.print_reference(libraries[args[1]]) + elif args[2] in ["author", "authors", "year"]: + utils.print_author_year(libraries[args[1]]) + else: raise Exception("Not recognized") @@ -141,17 +143,19 @@ if __name__ == "__main__": for i in range(len(libraries)): lib_name = libraries_names[i] lib = libraries[lib_name] - utils.export_abstract(lib, lib_name+".txt") + utils.export_author_year(lib, lib_name+".txt") elif nargs == 2: - utils.export_abstract(libraries[args[1]], args[1]+".txt") + utils.export_author_year(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"]: + elif args[2] in ["title"]: utils.export_title_author(libraries[args[1]], args[1]+".txt") - if args[2] in ["reference", "references", "ref", "refs"]: + elif args[2] in ["reference", "references", "ref", "refs"]: utils.export_reference(libraries[args[1]], args[1]+".txt") + elif args[2] in ["author", "authors", "year"]: + utils.export_author_year(libraries[args[1]], args[1]+".txt") else: raise Exception("Not recognized") @@ -181,14 +185,52 @@ if __name__ == "__main__": utils.arxtic_comment(args[2], libraries[args[2]]) s = False + elif arg0 in ["unread", "todo"]: + if nargs == 2: + block = utils.find(args[1], libraries["local"]) + known_libs = block["arxtic_library"] + if not "unread" in known_libs.split(","): + if "read" in known_libs.split(","): + utils.arxtic_library_remove(args[1], libraries["local"], "read") + utils.arxtic_library_add(args[1], libraries["local"], "unread") + s = False + + elif nargs == 3: + block = utils.find(args[1], libraries[args[2]]) + known_libs = block["arxtic_library"] + if not "unread" in known_libs.split(","): + if "read" in known_libs.split(","): + utils.arxtic_library_remove(args[1], libraries[args[2]], "read") + utils.arxtic_library_add(args[1], libraries[args[2]], "unread") + s = False + else: + raise Exception("Not recognized") + elif arg0 in ["read"]: if nargs == 2: utils.launch_reader(args[1], libraries["local"]) utils.arxtic_comment(args[1], libraries["local"]) + + block = utils.find(args[1], libraries["local"]) + known_libs = block["arxtic_library"] + if not "read" in known_libs.split(","): + if "unread" in known_libs.split(","): + utils.arxtic_library_remove(args[1], libraries["local"], "unread") + utils.arxtic_library_add(args[1], libraries["local"], "read") + utils.arxtic_date_read(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]]) + + block = utils.find(args[1], libraries[args[2]]) + known_libs = block["arxtic_library"] + if not "read" in known_libs.split(","): + if "unread" in known_libs.split(","): + utils.arxtic_library_remove(args[1], libraries[args[2]], "unread") + utils.arxtic_library_add(args[1], libraries[args[2]], "read") + utils.arxtic_date_read(args[1], libraries[args[2]]) s = False else: raise Exception("Not recognized") diff --git a/src/legacy.py b/src/legacy.py deleted file mode 100644 index 6b16a2f..0000000 --- a/src/legacy.py +++ /dev/null @@ -1,194 +0,0 @@ -#!/usr/bin/env python -#[TLP:AMBER] LIMITED DISTRIBUTION: WORK IN PROGRESS -""" -ArXtic: - -ArXtic queries arXiv and filters the output. - -@ Author: Moussouni, Yaël (MSc student; yael.moussouni@etu.unistra.fr) -@ Institution: Université de Strasbourg, CNRS, Observatoire astronomique - de Strasbourg, UMR 7550, F-67000 Strasbourg, France -@ Date: 2025-09-15 - -Licence: -ArXtic -Copyright (C) 2025 Yaël Moussouni (yael.moussouni@etu.unistra.fr) - -legacy.py -Copyright (C) 2025 Yaël Moussouni (yael.moussouni@etu.unistra.fr) - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -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 textwrap as tw -import feedparser as fp -import bibtexparser as bib -import requests as rq -import numpy as np -from urllib.parse import urlencode, quote_plus - -FILTERS_DIR = os.environ.get("FILTERS_DIR") -DB_DIR = os.environ.get("DB_DIR") -PDF_DIR = os.environ.get("PDF_DIR") -ARXIV_QUERY_URL = os.environ.get("ARXIV_QUERY_URL") -ARXIV_RSS_URL = os.environ.get("ARXIV_RSS_URL") -ADSABS_QUERY_URL = os.environ.get("ADSABS_QUERY_URL") -ADSABS_EXPORT_URL = os.environ.get("ADSABS_EXPORT_URL") -ADSABS_API_KEY = os.environ.get("ADSABS_API_KEY") - -COLOUR_DEFAULT="\033[0m" -COLOUR_INPUT="\033[36m" -COLOUR_OUTPUT="\033[32m" -COLOUR_INFO="\033[34m" -COLOUR_WARNING="\033[93m" -COLOUR_ERROR="\033[91m" - -## Filters - -def get_filters(): - filters = [] - filters_list = [f for f in os.listdir(FILTERS_DIR) if not f[0] == "."] - for i in range(len(filters_list)): - path = FILTERS_DIR + filters_list[i] - with open(path) as filter_file: - dic = {"fields": [], "values": [], "score": 1} - for line in filter_file.readlines(): - if "#FIELD" in line: - field = line.split("=")[1].replace("\"", "").strip() - dic["fields"].append(field) - elif "#SCORE" in line: - field = line.split("=")[1].strip() - dic["score"] = int(field) - elif line[0] == "#" or line in [" \n", "\n", ""]: - continue - else: - value = line.replace("\n", "") - dic["values"].append(value) - filters.append(dic) - return filters - -def filter_entries(filters, entries): - filtered_entries = [] - filtered_fields = [] - filtered_keywords = [] - filtered_score = [] - for entry in entries: - added = False - for filter_ in filters: - fields = filter_["fields"] - values = filter_["values"] - score = filter_["score"] - for field in fields: - for value in values: - if field in list(entry): - val = entry[field] - else: - val = "" - if not added and value.upper() in str(val).upper(): - filtered_entries.append(entry) - filtered_fields.append([field]) - filtered_keywords.append([value]) - filtered_score.append(score) - added = True - elif added and value.upper() in str(val).upper(): - filtered_score[-1] = filtered_score[-1] + score - if not field in filtered_fields[-1]: - filtered_fields[-1].append(field) - if not value in filtered_keywords[-1]: - filtered_keywords[-1].append(value) - filtered_data = {"fields": filtered_fields, - "keywords": filtered_keywords, - "score": filtered_score} - return filtered_entries, filtered_data - -if __name__ == "__main__": - read = file_to_bibtex("read.bib") - unread = file_to_bibtex("unread.bib") - library = file_to_bibtex("library.bib") - quit_action = False - - working_bibtex = bib.Library() - - while not quit_action: - read_keys = [b.key for b in read.blocks] - unread_keys = [b.key for b in unread.blocks] - library_keys = [b.key for b in library.blocks] - library_keys = [b.key for b in library.blocks] - - - print(COLOUR_INPUT + "Select an action") - action = input("> " + COLOUR_DEFAULT) - if action.upper() in ["QUIT", "EXIT", "Q"]: - quit_action = True - elif action in ["", " ", "help", "h"]: - print(COLOUR_OUTPUT - + "Available commands:\n" - + "\t- quit, exit, q: exit\n" - + COLOUR_DEFAULT) - - # Print - elif action.split(" ")[0].upper() in ["PRINT", "P"]: - if len(action.split(" ")) == 1: - print_bibtex(working_bibtex) - elif action.split(" ")[1].upper() == "READ": - print_bibtex(read) - elif action.split(" ")[1].upper() == "UNREAD": - print_bibtex(unread) - elif action.split(" ")[1].upper() == "LIBRARY": - print_bibtex(library) - else: - search_key = action.split(" ")[1] - - if search_key in read_keys: - print_bibtex(read.blocks[read_keys.index(search_key)]) - elif search_key in unread_keys: - print_bibtex(unread.blocks[unread_keys.index(search_key)]) - elif search_key in library_keys: - print_bibtex(library.blocks[library_keys.index(search_key)]) - else: - print(COLOUR_WARNING - + f"Warning: {search_key} cannot be found" - + COLOUR_DEFAULT) - # Clear - elif action.upper() in ["CLEAR", "CLEAN"]: - working_bibtex = bib.Library() - # Today - elif action.upper() in ["TODAY"]: - today_bibtex = today_arxiv() - working_bibtex.add(today_bibtex.blocks) - - # Library - elif action.upper() in ["LIBRARY"]: - library = list_pdf(library) - bibtex_to_file(library, "library.bib") - - # Arxiv - elif action.split(" ")[0].upper() == "ARXIV": - arxiv_ids = action.split(" ")[1:] - feed = get_arxiv_from_ids(arxiv_ids) - entries = get_arxiv_entries(feed) - for entry in entries: - bibtex_entry = arxiv_to_bibtex(entry, - arxtic_score=99) - working_bibtex.add(bibtex_entry.blocks) - - # ADS - elif action.split(" ")[0].upper() == "ADS": - ads_bibcode = "".join(action.split(" ")[1:]) - feed = get_ads_from_bibcode(ads_bibcode) - entries = get_ads_entries(feed) - for entry in entries: - bibtex_entry = ads_to_bibtex(entry, - arxtic_score=99) - working_bibtex.add(bibtex_entry.blocks) diff --git a/src/local_api.py b/src/local_api.py index 9563425..ed28254 100644 --- a/src/local_api.py +++ b/src/local_api.py @@ -66,6 +66,17 @@ def bibtex_to_file(bibtex, filename, directory=DB_DIR): bibentry = bib.write_file(directory+filename, bibtex) return bibtex +def update_library(source_library, lib_name): + new_library = bib.Library() + for block in source_library.blocks: + if isinstance(block, bib.model.Block): + arxtic_library = block["arxtic_library"] + if lib_name in arxtic_library.split(","): + new_library.add(block) + return new_library + + + def update_local_pdf(library=None, directory=PDF_DIR): # TODO: delete entry in library if the PDF file is deleted. if library is None: diff --git a/src/utils.py b/src/utils.py index 9a72d50..10e775d 100644 --- a/src/utils.py +++ b/src/utils.py @@ -61,10 +61,28 @@ COLOUR_ARXTIC = "\033[91m" ## General -def wrap(txt, length=80): - wrapped_txt = '\n'.join(tw.wrap(txt, length, break_long_words=False)) +def wrap(txt, length=80, keep_line_break = False): + if keep_line_break: + wrapped_table = [] + for line in txt.split("\n"): + wrapped_line = '\n'.join(tw.wrap(line, length, break_long_words=False)) + wrapped_table += [wrapped_line] + wrapped_txt = "\n".join(wrapped_table) + + else: + wrapped_txt = '\n'.join(tw.wrap(txt, length, break_long_words=False)) return wrapped_txt +def parse_authors(authors): + author_list = authors.replace("{", "").replace("}", "").split(" and ") + N = len(author_list) + if N == 1: + return author_list[0] + elif N == 2: + return f"{author_list[0]} & {author_list[1]}" + else: + return f"{author_list[0]} et al." + ## Prints def print_abstract(library): @@ -72,7 +90,7 @@ def print_abstract(library): library = bib.Library(library) for block in library.blocks: if isinstance(block, bib.model.Block): - print("#" + print("# " + COLOUR_REF + block.key + " [" + block["url"] + "]" + COLOUR_DEFAULT) @@ -98,7 +116,7 @@ def print_abstract(library): if block["arxtic_comment"] != "": print("Comment: " + COLOUR_ARXTIC - + wrap(block["arxtic_comment"]) + + wrap(block["arxtic_comment"], keep_line_break=True) + COLOUR_DEFAULT) if block["arxtic_library"] != "": print("Library: " @@ -130,7 +148,7 @@ def print_title_author(library): library = bib.Library(library) for block in library.blocks: if isinstance(block, bib.model.Block): - print("#" + print("# " + COLOUR_REF + block.key + " [" + block["url"] + "]" + COLOUR_DEFAULT) @@ -152,7 +170,7 @@ def print_title_author(library): if block["arxtic_comment"] != "": print("Comment: " + COLOUR_ARXTIC - + wrap(block["arxtic_comment"]) + + wrap(block["arxtic_comment"], keep_line_break=True) + COLOUR_DEFAULT) if block["arxtic_library"] != "": print("Library: " @@ -190,6 +208,60 @@ def print_reference(library): print(COLOUR_DEFAULT) return 0 +def print_author_year(library): + if not isinstance(library, bib.Library): + library = bib.Library(library) + for block in library.blocks: + if isinstance(block, bib.model.Block): + print("# " + + COLOUR_REF + + block.key + " [" + block["url"] + "]" + + COLOUR_DEFAULT) + if block["arxtic_filename"] != "": + print(COLOUR_REF + + "(" + + PDF_DIR + + block["arxtic_filename"] + + ")" + + COLOUR_DEFAULT) + print("Article: " + + COLOUR_AUTHOR + + wrap(parse_authors(block["author"]) + + " (" + block["year"] + ")") + + COLOUR_DEFAULT) + print("Title: " + + COLOUR_TITLE + + wrap(block["title"]) + + COLOUR_DEFAULT) + if block["arxtic_comment"] != "": + print("Comment: " + + COLOUR_ARXTIC + + wrap(block["arxtic_comment"], keep_line_break=True) + + 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 ## Exports def export_abstract(library, filename, directory=PDF_DIR): @@ -198,14 +270,15 @@ def export_abstract(library, filename, directory=PDF_DIR): 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("# " + 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"]) + file.write(wrap("Comment: " + block["arxtic_comment"], + keep_line_break=True) + "\n") if block["arxtic_library"] != "": file.write("Library: " @@ -229,13 +302,14 @@ def export_title_author(library, filename, directory=PDF_DIR): 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("# " + 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"]) + file.write(wrap("Comment: " + block["arxtic_comment"], + keep_line_break=True) + "\n") if block["arxtic_library"] != "": file.write("Library: " @@ -259,9 +333,41 @@ def export_reference(library, filename, directory=PDF_DIR): 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("# " + block.key + " [" + block["url"] + "]\n") return 0 +def export_author_year(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("Article: " + + parse_authors(block["author"]) + + " (" + block["year"] + ")") + + "\n") + file.write(wrap("Title: " + block["title"]) + "\n") + + if block["arxtic_comment"] != "": + file.write(wrap("Comment: " + block["arxtic_comment"], + keep_line_break=True) + + "\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 ## Manipulation def find(key, library):