writing: Restructure module layout per-program
Similarly to the qutebrowser module we change the layout to have a program name at the top-level and all required files for that specific program within, whether they reside within .config, .local or anywhere else. We use dotter mappings to achieve this.
This commit is contained in:
parent
0903e7e443
commit
85c152a07c
19 changed files with 8 additions and 2 deletions
74
writing/papis/config
Normal file
74
writing/papis/config
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
[settings]
|
||||
default-library = main
|
||||
formater = bbt
|
||||
local-config-file = .papis.config
|
||||
|
||||
opentool = sioyek
|
||||
picktool = papis-tui
|
||||
mark-opener-format = sioyek --page {mark[value]}
|
||||
file-browser = vifm
|
||||
|
||||
# edit info.yaml as new papers are added
|
||||
add-edit = True
|
||||
# ref-format = {doc[author_list][0][family]}{doc[year]}
|
||||
ref-format = bbt
|
||||
add-folder-name = {doc[author_list][0][family]}-{doc[title]}
|
||||
add-file-name = {doc[author_list][0][family]}{doc[year]}
|
||||
header-format-file = ~/.config/papis/headerformat
|
||||
extra-bibtex-keys = ["tags", "readstatus", "priority"]
|
||||
|
||||
database-backend = whoosh
|
||||
whoosh-schema-fields = ["doi", "ref", "author", "year", "title", "publisher", "tags", "readstatus", "date", "isbn", "type", "keyword", "qualityassured", "priority"]
|
||||
# to make whoosh list everything by default
|
||||
default-query-string = *
|
||||
|
||||
notes-name = notes.md
|
||||
notes-template = {doc[author_list][0][family]}{doc[year]}--{doc[title]}
|
||||
|
||||
[tui]
|
||||
editmore = vi
|
||||
|
||||
[main]
|
||||
dir = ~/documents/library
|
||||
|
||||
# My personal reading
|
||||
[personal]
|
||||
dir = ~/documents/library/personal
|
||||
|
||||
# Sustainable supply chain logistics, especially procurement
|
||||
[litrev-rahman]
|
||||
dir = ~/documents/library/litrev-rahman
|
||||
|
||||
# Addressing Inequalities in World of Work research
|
||||
[ilo]
|
||||
dir = ~/documents/library/ilo-wow
|
||||
|
||||
# fediverse-related academia, from
|
||||
# https://www.zotero.org/groups/5490166/fediverse_research
|
||||
[fedi]
|
||||
dir = ~/documents/library/fediverse
|
||||
|
||||
# General computer science reading
|
||||
[cs]
|
||||
dir = ~/documents/library/cs
|
||||
|
||||
# Electrical engineering reading
|
||||
[ee]
|
||||
dir = ~/documents/library/ee
|
||||
|
||||
# All my university programme readings
|
||||
[emgs]
|
||||
dir = ~/documents/library/emgs
|
||||
|
||||
# General research reading
|
||||
[academia]
|
||||
dir = ~/documents/library/academia
|
||||
|
||||
|
||||
[plugins.extract]
|
||||
tags = {"red": "important", "green": "extra", "blue": "toread"}
|
||||
|
||||
[plugins.bbt-formatter]
|
||||
full-year = True
|
||||
title-words = 2
|
||||
title-chars = 20
|
||||
3
writing/papis/config.py
Normal file
3
writing/papis/config.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# from papis import config
|
||||
#
|
||||
# config.set("ref-format", f"{doc[author].split()[0] if 'author' in doc else 'Unknown'}{doc[year] if 'year' in doc else '0000'}")
|
||||
3
writing/papis/headerformat
Normal file
3
writing/papis/headerformat
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{doc.html_escape[title]}
|
||||
<ansigreen>{doc.html_escape[author]}</ansigreen>
|
||||
<ansiblue>({doc.html_escape[year]})</ansiblue> [<ansiyellow>{doc.html_escape[tags]}</ansiyellow>] [<ansired>{doc.html_escape[readstatus]}</ansired>]
|
||||
203
writing/papis/papistui.yaml
Normal file
203
writing/papis/papistui.yaml
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
# current issues/requests
|
||||
# - [ ] call arbitrary shell command
|
||||
# - [ ] non-blocking opening of files
|
||||
# - [ ] distinguish in config between aliases for multiline/table
|
||||
# - [ ] update view after more operations
|
||||
# - if I call an 'update' command (e.g. set new tag or read status), it will not update in list until 'edit' command invoked and rebuilding the list
|
||||
# - two selection additions:
|
||||
# - [ ] select all in view
|
||||
# - [ ] limit view to currently selected
|
||||
#
|
||||
# for papis itself:
|
||||
# - [ ] I can not search custom numeric fields in whoosh? i.e. 'priority:1' will not give results. 'priority:high' will
|
||||
base:
|
||||
vimflavour: nvim
|
||||
documentlist:
|
||||
defaultstyle: multiline
|
||||
marked-icon: ""
|
||||
multilinestyle:
|
||||
rows:
|
||||
# TODO: show if already note attached; have info window display note content?
|
||||
- "{' ' if 'priority' not in doc or doc['priority'] == 'low' else '<red> </red>' if doc['priority'] == 'high' else '<yellow> </yellow>'}<cyan>{doc.alias('type')} {doc['ref']}</cyan> {doc.alias('readstatus')} {' ' if doc['notes'] else ''}"
|
||||
- "<white><bold>{doc.html_escape['title']}</bold></white>"
|
||||
- "<blue>{doc.html_escape['author']}</blue>"
|
||||
- "{doc.foreach('tags', '<lightgray>(</lightgray><gray><dim>{}</dim></gray><lightgray>)</lightgray>', split = ', ', sep = ' ')}"
|
||||
tablestyle:
|
||||
separator: " "
|
||||
headerstyle: "underline|bold"
|
||||
rowstyle: "white_bg"
|
||||
cursorrowstyle: "black_white|bold"
|
||||
columns:
|
||||
- {
|
||||
header: "",
|
||||
content: "{'' if 'priority' not in doc or doc['priority'] == 'low' else '' if doc['priority'] == 'high' else ''}",
|
||||
width: 1,
|
||||
}
|
||||
- {
|
||||
header: "",
|
||||
content: "{'' if doc['readstatus'] == 'read' else '' if doc['readstatus'] == 'skimmed' else ' '}",
|
||||
width: 1,
|
||||
}
|
||||
- {
|
||||
header: "",
|
||||
content: "{'' if doc['notes'] else '' if doc['note'] else ' '}",
|
||||
width: 1,
|
||||
}
|
||||
- {
|
||||
header: "",
|
||||
content: "{doc.alias('type')}",
|
||||
width: 1,
|
||||
}
|
||||
- { header: "Authors", content: "{doc['author']}", width: 25 }
|
||||
- { header: "Year", content: "{str(doc['year'])}", width: 4 }
|
||||
- { header: "Title", content: "{doc['title']}", width: 125 }
|
||||
- { header: "Reference", content: "{doc['ref']}", width: 15 }
|
||||
- {
|
||||
header: "",
|
||||
content: "{str(len(doc.get_files()) if len(doc.get_files()) > 0 else '')}",
|
||||
width: 1,
|
||||
}
|
||||
- { header: "Tags", content: "{doc['tags'] if isinstance(doc['tags'], str) else doc.foreach('tags', '{}', sep=', ')}", width: 35 }
|
||||
aliases:
|
||||
type:
|
||||
{
|
||||
article: "",
|
||||
book: "",
|
||||
inbook: "",
|
||||
incollection: "",
|
||||
software: "",
|
||||
presentation: "",
|
||||
thesis: "",
|
||||
techreport: "",
|
||||
_default_: "",
|
||||
}
|
||||
readstatus:
|
||||
{
|
||||
read: "<green></green>",
|
||||
skimmed: "<yellow></yellow>"
|
||||
}
|
||||
|
||||
keymappings:
|
||||
q: quit
|
||||
"?": help
|
||||
T: toggle_style
|
||||
S: cmd "sort "
|
||||
/: search_mode
|
||||
<key_down>: scroll_down
|
||||
<key_up>: scroll_up
|
||||
<ctrl-f>: page_down
|
||||
<ctrl-b>: page_up
|
||||
G: jump_to_bottom
|
||||
gg: jump_to_top
|
||||
j: scroll_down
|
||||
k: scroll_up
|
||||
o: open -r "pdf$"
|
||||
O: open -d
|
||||
b: browse
|
||||
B: papis browse -k doi papis_id:{doc['papis_id']}
|
||||
R: view_reset
|
||||
<c-r>: reload
|
||||
e:
|
||||
- edit
|
||||
- edit info
|
||||
n:
|
||||
- papis edit -n papis_id:{doc['papis_id']}
|
||||
- edit notes
|
||||
"'n":
|
||||
- search "notes:.+"
|
||||
- limit to entries with notes
|
||||
"'u":
|
||||
- search "readstatus:read OR readstatus:skimmed"
|
||||
- limit to read entries
|
||||
"'r":
|
||||
- search "NOT readstatus:read AND NOT readstatus:skimmed"
|
||||
- limit to unread entries
|
||||
" ": mark_selected
|
||||
mm: mark_selected
|
||||
M: mark_down
|
||||
J: mark_down
|
||||
mu: unmark_all
|
||||
mv: mark_view
|
||||
",t": cmd "tag "
|
||||
",r":
|
||||
- papis update -s readstatus read papis_id:{doc['papis_id']}
|
||||
- set readstatus read
|
||||
",k":
|
||||
- papis update -s readstatus skimmed papis_id:{doc['papis_id']}
|
||||
- set readstatus skimmed
|
||||
",u":
|
||||
- papis update -s readstatus "" papis_id:{doc['papis_id']}
|
||||
- set readstatus unread
|
||||
",ph":
|
||||
- papis update -s priority high papis_id:{doc['papis_id']}
|
||||
- set priority high
|
||||
",pm":
|
||||
- papis update -s priority medium read papis_id:{doc['papis_id']}
|
||||
- set priority medium
|
||||
",pl":
|
||||
- papis update -s priority low read papis_id:{doc['papis_id']}
|
||||
- set priority low
|
||||
i:
|
||||
- info_toggle
|
||||
- "Toggle info window"
|
||||
I:
|
||||
- info_cycle
|
||||
- "Cycle info windows"
|
||||
<ctrl-p>: info_scroll_up
|
||||
<ctrl-n>: info_scroll_down
|
||||
# all require 'clip' script to be available on PATH
|
||||
yy:
|
||||
- copy_to_clipboard "[@{doc['ref']}]"
|
||||
- yank pandoc-styled reference
|
||||
ss:
|
||||
- vim_send "[@{doc['ref']}]"
|
||||
- send vim pandoc-styled reference
|
||||
yl:
|
||||
- copy_to_clipboard "\\cite\{{doc['ref']}\}"
|
||||
- yank latex-styled reference
|
||||
sl:
|
||||
- vim_send "\\cite\{{doc['ref']}\}"
|
||||
- send vim latex-styled reference
|
||||
yr:
|
||||
- copy_to_clipboard "{format_reference(doc,style='apa')}"
|
||||
- yank apa-styled reference
|
||||
sr:
|
||||
- vim_send "{format_reference(doc,style='apa')}"
|
||||
- send vim apa-styled reference
|
||||
yt:
|
||||
- copy_to_clipboard "{doc['title']}"
|
||||
- yank title
|
||||
yu:
|
||||
- copy_to_clipboard "{doc['url']}"
|
||||
- yank url
|
||||
yd:
|
||||
- copy_to_clipboard "{doc['doi']}"
|
||||
- yank doi
|
||||
# TODO look into https://github.com/supersambo/papis-tui vim-send mappings
|
||||
|
||||
infowindow:
|
||||
default_on: False
|
||||
views:
|
||||
doc:
|
||||
content: "author: {doc['author'].strip()}\n title: {doc['title'].strip()}\n tags:{doc['tags'] if isinstance(doc['tags'], str) else doc.foreach('tags', '{}', sep=', ')}"
|
||||
height: 8
|
||||
apa:
|
||||
content: "{format_reference(doc,style='apa')}"
|
||||
abstract:
|
||||
content: "{doc['abstract']}"
|
||||
linewrap: True
|
||||
height: 8
|
||||
|
||||
commandline:
|
||||
search:
|
||||
keyword_aliases: { a: "author:", t: "title:", y: "year:", k: "tags:" }
|
||||
|
||||
statusbar:
|
||||
left:
|
||||
default: "<black_green><bold> {info['mode_upper']} </black_green></bold><green_bg></green_bg>"
|
||||
normal: "<black_green><bold> {info['mode_upper']} </black_green></bold><green_bg></green_bg>"
|
||||
command: "<black_cyan><bold> {info['mode_upper']} </black_cyan></bold><cyan_bg></cyan_bg>"
|
||||
select: "<black_red><bold> {info['mode_upper']} </black_red></bold><red_bg></red_bg>"
|
||||
search: "<black_magenta><bold> {info['mode_upper']} </black_magenta></bold><magenta_bg></magenta_bg>"
|
||||
right:
|
||||
default: "<green>{info['sortkeys']} </green><cyan_bg></cyan_bg><black_cyan> {info['idx']} < {info['marked']} < {info['view']} < {info['items']} </black_cyan>"
|
||||
163
writing/papis/scripts/papis-marvin
Executable file
163
writing/papis/scripts/papis-marvin
Executable file
|
|
@ -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())
|
||||
28
writing/papis/scripts/papis-reload
Executable file
28
writing/papis/scripts/papis-reload
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env python
|
||||
# papis-short-help: Manually rebuild cache for all libraries
|
||||
#
|
||||
# This tiny script updates all libraries by rebuilding their caches.
|
||||
# Useful to invoke after manual edits in one of your library folders
|
||||
# if you have many 'sub-libraries' (one library location with sub-
|
||||
# directories).
|
||||
# You don't have to think about which library you changed stuff in
|
||||
# and just get everything updated. Might take a little time but
|
||||
# should generally be a quick process ().
|
||||
|
||||
import papis.api
|
||||
from argparse import ArgumentParser
|
||||
|
||||
parser = ArgumentParser()
|
||||
_ = parser.add_argument(
|
||||
"--all", "-a", help="reload all libraries not just current", action="store_true"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.all:
|
||||
libs = papis.api.get_libraries()
|
||||
else:
|
||||
libs = [papis.api.get_lib_name()]
|
||||
|
||||
for lib in libs:
|
||||
papis.api.clear_lib_cache(lib)
|
||||
_ = papis.api.get_all_documents_in_lib()
|
||||
116
writing/papis/scripts/papis-show
Executable file
116
writing/papis/scripts/papis-show
Executable file
|
|
@ -0,0 +1,116 @@
|
|||
#!/usr/bin/env python
|
||||
# papis-short-help: Display pretty human-readable document overview
|
||||
#
|
||||
# Takes a query and displays the metadata of the results. Uses
|
||||
# python-rich to display pretty panels if it exists in the environment,
|
||||
# otherwise displays regular text.
|
||||
# Can be invoked with -s to display single-line results better for
|
||||
# pasting into other documents.
|
||||
|
||||
import argparse
|
||||
from importlib.util import find_spec
|
||||
from papis import database
|
||||
from papis.database.base import Database
|
||||
from papis.document import Document
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--short", "-s", help="only display single-line quick info", action="store_true"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--url",
|
||||
dest="identifier",
|
||||
help="display identifier (doi, isbn, url) or not",
|
||||
action=argparse.BooleanOptionalAction,
|
||||
default=True,
|
||||
)
|
||||
parser.add_argument("query", nargs="*", help="the query to search for")
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
@dataclass
|
||||
class DocInfo:
|
||||
author: str = ""
|
||||
title: str = ""
|
||||
year: str = ""
|
||||
journal: str = ""
|
||||
volume: str = ""
|
||||
issue: str = ""
|
||||
pages: str = ""
|
||||
publisher: str = ""
|
||||
tags: list = field(default_factory=lambda: [])
|
||||
dtype: str = ""
|
||||
identifier: str = ""
|
||||
doi: str = ""
|
||||
|
||||
@staticmethod
|
||||
def from_Document(doc: Document):
|
||||
t: list[str] | str = doc.get("tags", "")
|
||||
tags = (
|
||||
t.replace(";", ",").replace(" ", "").split(",") if isinstance(t, str) else t
|
||||
)
|
||||
return DocInfo(
|
||||
author=doc.get("author", ""),
|
||||
title=doc.get("title", ""),
|
||||
year=doc.get("year", ""),
|
||||
journal=doc.get("journal", ""),
|
||||
volume=doc.get("volume", ""),
|
||||
issue=doc.get("issue", ""),
|
||||
pages=doc.get("pages", ""),
|
||||
publisher=doc.get("publisher", ""),
|
||||
tags=tags,
|
||||
dtype=doc.get("type", ""),
|
||||
identifier=doc.get("doi", doc.get("isbn", doc.get("url", ""))),
|
||||
)
|
||||
|
||||
|
||||
def main(db: Database, args) -> None:
|
||||
query = " ".join(args.query)
|
||||
docs: list[Document] = db.query(query)
|
||||
for doc in docs:
|
||||
info = DocInfo.from_Document(doc)
|
||||
if args.short:
|
||||
print_short(info, with_identifier=args.identifier)
|
||||
else:
|
||||
print_info(info, with_identifier=args.identifier)
|
||||
|
||||
|
||||
def print_short(doc: DocInfo, with_identifier: bool = True) -> None:
|
||||
print(
|
||||
f"{doc.author} ({doc.year}). {doc.title}. "
|
||||
f"{doc.identifier if with_identifier else ''}\n"
|
||||
)
|
||||
|
||||
|
||||
def print_info(doc: DocInfo, with_identifier: bool = True) -> None:
|
||||
if find_spec("rich"):
|
||||
from rich import print as richprint
|
||||
from rich.panel import Panel
|
||||
|
||||
info: str = (
|
||||
f"[red]{doc.author}[/red] ({doc.year}) "
|
||||
f"[steel_blue]\\[{doc.dtype}][/steel_blue]\n"
|
||||
f"[bold]{doc.title}[/bold]\n"
|
||||
f"{doc.journal} ({doc.volume}/{doc.issue}){doc.pages} - {doc.publisher}\n"
|
||||
f"[grey69]{[tag for tag in doc.tags]} "
|
||||
)
|
||||
if with_identifier:
|
||||
info += f"[link={doc.identifier}]{doc.identifier}[/link][/grey69]"
|
||||
richprint(Panel(info, expand=False))
|
||||
else:
|
||||
info: str = (
|
||||
f"{doc.author} ({doc.year}) "
|
||||
f"[{doc.dtype}]\n"
|
||||
f"{doc.title}\n"
|
||||
f"{doc.journal} ({doc.volume}/{doc.issue}){doc.pages} - {doc.publisher}\n"
|
||||
f"{[tag for tag in doc.tags]} "
|
||||
f"{doc.identifier}"
|
||||
if with_identifier
|
||||
else ""
|
||||
)
|
||||
print(f"{info}\n---")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(database.get(), args)
|
||||
57
writing/papis/scripts/papis-tags
Executable file
57
writing/papis/scripts/papis-tags
Executable file
|
|
@ -0,0 +1,57 @@
|
|||
#!/usr/bin/env python
|
||||
# papis-short-help: List all tags occuring in query items
|
||||
#
|
||||
# Takes a query and spits out a sorted list of all tags contained therein,
|
||||
# nothing more.
|
||||
# Can be very useful for things like picking a tag or two and listing all
|
||||
# items that contain it:
|
||||
# $ papis tags "*" | fzf | xargs papis show "tags:{}"
|
||||
|
||||
import argparse
|
||||
from papis import database
|
||||
from papis.database.base import Database
|
||||
|
||||
from papis.document import Document
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--count", "-c", help="the query to search for", action="store_true"
|
||||
)
|
||||
parser.add_argument("query", nargs="*", help="the query to search for", default="*")
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
def main(db: Database, args) -> None:
|
||||
query = " ".join(args.query)
|
||||
docs: list[Document] = db.query(query)
|
||||
|
||||
all_tags: dict[str, int] = {}
|
||||
for doc in docs:
|
||||
t: list[str] | str = doc.get("tags", "")
|
||||
tags = (
|
||||
t.replace(";", ",").replace(" ", "").split(",") if isinstance(t, str) else t
|
||||
)
|
||||
|
||||
for tag in tags:
|
||||
if tag == '':
|
||||
continue
|
||||
all_tags[tag] = all_tags.get(tag, 0) + 1
|
||||
|
||||
if args.count:
|
||||
print_tags_and_counts(all_tags)
|
||||
else:
|
||||
print_tags_only(all_tags)
|
||||
|
||||
def print_tags_only(all_tags):
|
||||
for tag in sorted(all_tags):
|
||||
print(tag)
|
||||
|
||||
def print_tags_and_counts(all_tags):
|
||||
for tag, count in sorted(all_tags.items(), key=lambda d: d[1], reverse=True):
|
||||
if args.count:
|
||||
print(tag, count)
|
||||
else:
|
||||
print(tag)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(database.get(), args)
|
||||
Loading…
Add table
Add a link
Reference in a new issue