papis-extract/papis_extract/formatter.py

124 lines
3.3 KiB
Python
Raw Normal View History

from typing import Protocol
from papis.document import Document
from papis_extract.annotation import Annotation
2024-06-12 09:46:39 +00:00
class Formatter(Protocol):
"""Basic formatter protocol.
Every valid formatter must implement at least this protocol.
A formatter is a function which receives a document and a list
of annotations and spits them out in some formatted way.
Formatters additionally must take the (often optional) passed
parameter 'first' which signals to the formatter that the current
document entry is the very first one to be printed in whatever
exporter is used, if multiple entries are printed.
This can be useful for adding a header if necessary for the format.
"""
2024-06-12 09:46:39 +00:00
def __call__(
self, document: Document, annotations: list[Annotation], first: bool
) -> str: ...
def format_markdown(
document: Document = Document(),
annotations: list[Annotation] = [],
first: bool = False,
headings: str = "setext", # setext | atx | None
) -> str:
if not annotations:
return ""
template = (
"{{#tag}}#{{tag}}\n{{/tag}}"
"{{#quote}}> {{quote}}{{/quote}}{{#page}} [p. {{page}}]{{/page}}"
"{{#note}}\n NOTE: {{note}}{{/note}}"
)
output = ""
heading = f"{document.get('title', '')} - {document.get('author', '')}"
if headings == "atx":
output += f"# {heading}\n\n"
elif headings == "setext":
title_decoration = (
f"{'=' * len(document.get('title', ''))} "
f"{'-' * len(document.get('author', ''))}"
)
output += f"{title_decoration}\n{heading}\n{title_decoration}\n\n"
for a in annotations:
output += a.format(template)
output += "\n\n"
output += "\n\n\n"
return output.rstrip()
def format_markdown_atx(
document: Document = Document(),
annotations: list[Annotation] = [],
first: bool = False,
) -> str:
return format_markdown(document, annotations, headings="atx")
def format_markdown_setext(
document: Document = Document(),
annotations: list[Annotation] = [],
first: bool = False,
) -> str:
return format_markdown(document, annotations, headings="setext")
def format_count(
document: Document = Document(),
annotations: list[Annotation] = [],
first: bool = False,
) -> str:
if not annotations:
return ""
count = 0
for _ in annotations:
count += 1
return (
f"{count} "
f"{document.get('author', '')}"
f"{': ' if 'author' in document else ''}" # only put separator if author
f"{document.get('title', '')}"
).rstrip()
def format_csv(
document: Document = Document(),
annotations: list[Annotation] = [],
first: bool = False,
) -> str:
header: str = "type,tag,page,quote,note,author,title,ref,file"
template: str = (
'{{type}},{{tag}},{{page}},"{{quote}}","{{note}}",'
'"{{doc.author}}","{{doc.title}}","{{doc.ref}}","{{file}}"'
)
output = f"{header}\n" if first else ""
if not annotations:
return ""
for a in annotations:
output += a.format(template, doc=document)
output += "\n"
return output.rstrip()
formatters: dict[str, Formatter] = {
"count": format_count,
"csv": format_csv,
"markdown": format_markdown,
"markdown-atx": format_markdown_atx,
"markdown-setext": format_markdown_setext,
}