Module betterletter.main

This is the actual script.

Expand source code
"""This is the actual script."""
import sys
import os
import uuid
import re
from time import sleep
from datetime import datetime, timedelta
import typer
import yaml
import frontmatter
import markdown
from pathlib import Path
from fpdf import FPDF, HTMLMixin
from prompt_toolkit import PromptSession

# from prompt_toolkit import prompt
from prompt_toolkit.completion import WordCompleter
from rich import print
from rich import box
from rich.panel import Panel
from rich.console import Console
from rich.markdown import Markdown
from rich.prompt import Confirm
from rich.table import Table
from simple_term_menu import TerminalMenu
from rich.status import Status
from rich.traceback import install

install()  # rich tracebacks
# install(show_locals=True)  # rich tracebacks

COLOR = "bright_black"

APP_NAME = "betterletter"
VON = Path("betterletter_from.md")
TO = Path("betterletter_to.md")
BODY = Path("betterletter_body.md")
PDF_FILE = Path("betterletter_POC.pdf")
EDITORS = ["vi", "gedit", "codium", "nano", "kate", "vim", "nvim", "atom"]
HOTKEYS = [
    "[1] ",
    "[2] ",
    "[3] ",
    "[4] ",
    "[5] ",
    "[6] ",
    "[7] ",
    "[8] ",
    "[9] ",
    "[0] ",
    "[a] ",
    "[s] ",
    "[d] ",
    "[f] ",
    "[g] ",
    "[y] ",
    "[x] ",
    "[c] ",
    "[v] ",
    "[w] ",
    "[e] ",
    "[r] ",
    "[t] ",
    "[z] ",
    "[u] ",
    "[i] ",
    "[o] ",
    "[p] ",
    "[b] ",
    "[n] ",
    "[m] ",
]

app_dir = typer.get_app_dir(APP_NAME)
config_path: Path = Path(app_dir) / "addressees.yml"

if not config_path.is_file():
    typer.echo("Config file doesn't exist.")
    # sleep(5)
    addressees = {}
    if not os.path.isdir(app_dir):
        os.mkdir(app_dir)
    with open(config_path, "w") as f:
        yaml.dump(addressees, f)

app = typer.Typer()  # initiate typer app


class Editor:
    """Configuration Class."""

    # Generating Completions
    completer_list = []

    if os.path.isfile(VON):
        with open(VON) as f:
            von_frontmatter = frontmatter.load(f)
            temp_dict = von_frontmatter.to_dict()
            del temp_dict["content"]
            del temp_dict["document"]
            for key in temp_dict:
                words = temp_dict[key].split()
                if words != []:
                    completer_list.extend(words)

    if os.path.isfile(TO):
        with open(TO) as f:
            von_frontmatter = frontmatter.load(f)
            temp_dict = von_frontmatter.to_dict()
            del temp_dict["content"]
            del temp_dict["document"]
            del temp_dict["uuid"]
            for key in temp_dict:
                words = temp_dict[key].split()
                if words != []:
                    completer_list.extend(words)

    if os.path.isfile(BODY):
        with open(BODY) as f:
            von_frontmatter = frontmatter.load(f)
            temp_dict = von_frontmatter.to_dict()
            del temp_dict["document"]
            for line in temp_dict:
                words = temp_dict[line].split()
                if words != []:
                    completer_list.extend(words)

    # removing duplicate words
    completer_list = list(dict.fromkeys(completer_list))
    test_completer = WordCompleter(sorted(completer_list))
    favourite = "vi"
    cons = Console()
    sess = PromptSession(
        message="❯ ", wrap_lines=True, vi_mode=True, completer=test_completer
    )
    last = {}
    if os.path.isfile("betterletter_make.yml"):
        with open("betterletter_make.yml") as f:
            last = yaml.load(f, Loader=yaml.FullLoader)
    adressees = {}
    with open(config_path) as f:
        addressees = yaml.load(f, Loader=yaml.FullLoader)


ed = Editor()


@app.callback(invoke_without_command=True)
def callback(ctx: typer.Context):
    """This is the callback function invoked when the app is run without any commands.

    By default this just shows the --help. Here however the otion to run the app without any commands has been enabled.
    """
    ed.cons.rule(title="better letter", align="right", style=COLOR)
    sys.stdout.write("\x1b]2;" + "Betterletter" + "\x07")
    if ctx.invoked_subcommand is None:  # invoked without commands.
        ed.cons.print(ctx.get_help())


@app.command()
def sender():
    """Work with the sender information"""
    current_dir = os.getcwd()
    # try:
    #     dirs_from_conf = conf["dirs"]
    #     if current_dir not in dirs_from_conf:

    #         cons.print(f'{current_dir} is not in congfiguration. Would you like to add it?')

    # except KeyError:
    #     cons.print(f'Setting up {current_dir} as a working directory.')
    #     dirs_from_conf = [current_dir]
    #     with open(config_path, 'w') as f: # safe
    #         yaml.dump(conf, f)
    if os.path.isfile(VON):
        with open(VON) as f:
            von_frontmatter = frontmatter.load(f)
    else:
        von_frontmatter = frontmatter.Post(
            """# Sender Information

Please confirm the following data

- Title - Herr, Frau, Professor Dr who or whatever or just leave it blank.
- First name
- Last name
- Address 1 - Usually street and number.
- Address 2 - Additional information. Often just blank.
- Address 3 - Post code, wich is the [5 digit number if you are in
Germany](https://en.wikipedia.org/wiki/List_of_postal_codes_in_Germany) and city.
Sometimes prepended with a country code, i.e. `DE` for Germany.
- Country - in English for international delivery sometimes the country code is found here: `DE Germany`
"""
        )
        von_frontmatter["document"] = "Sender Information"
        with open(VON, "w") as f:
            f.writelines(frontmatter.dumps(von_frontmatter))
    ed.cons.print(Markdown(von_frontmatter.content))
    from_fields = [
        "title",
        "first_name",
        "last_name",
        "address_1",
        "address_2",
        "post_code",
        "city",
        "country",
        "phone",
        "email",
        "web_site",
    ]
    ed.cons.print()
    for von_item in from_fields:
        try:
            ed.cons.rule(
                title=f'[green]{von_frontmatter[von_item]} [{COLOR}]❮ {von_item.replace("_", " ").capitalize()}',
                style=COLOR,
                align="left",
                characters="❮",
            )
        except KeyError as identfier:
            von_frontmatter[von_item] = ed.sess.prompt(
                default=von_item.replace("_", " ").capitalize()
            )
    continue_on_one = 1
    while continue_on_one == 1:
        continue_on_one = menu(["[1] OK", "[2] Edit Field"])
        if continue_on_one == 1:
            choices = []
            items = []
            i = 0
            for item in von_frontmatter.keys():
                if item in from_fields:
                    items.append(item)
                    choices.append(
                        f'{HOTKEYS[i]}{item.replace("_", " ").capitalize()}: {von_frontmatter[item]}'
                    )
                i += 1
            try:
                choice = menu(prompt="Field?", choices=choices)

                von_frontmatter[items[choice]] = ed.sess.prompt(
                    default=von_frontmatter[items[choice]]
                )
                ed.cons.rule(
                    title=f'[green]{von_frontmatter[items[choice]]} [{COLOR}]❮ {items[choice].replace("_", " ").capitalize()}',
                    style=COLOR,
                    align="left",
                    characters="❮",
                )
            except TypeError:
                pass
        else:
            ed.cons.print()
            ed.cons.rule(
                title=f"[{COLOR}]Sender Information checked and recorded in {str(VON)} ",
                style=COLOR,
                align="right",
            )

    with open(VON, "w") as f:
        f.writelines(frontmatter.dumps(von_frontmatter))


@app.command()
def address(
    address_book: bool = typer.Option(
        False,
        "--book",
        "-b",
        help="Address Book.",
    ),
):
    """Addressee information."""
    current_dir = os.getcwd()
    myuuid = str(uuid.uuid4())
    # print(myuuid)
    if address_book:
        for a_uuid in ed.addressees:
            add = ed.addressees[a_uuid]
            panel = Panel(
                renderable=f'[bold]{add["last_name"].upper()} {add["first_name"]}[/] {add["title"]}'.strip(),
                title=f'{add["address_1"]} [bold]{add["city"].strip()}',
                title_align="left",
                subtitle=f'{add["address_2"]} {add["city"]}'.strip(),
                subtitle_align="right",
                expand=False,
                style=COLOR,
                border_style=COLOR,
                # width: Optional[int]=None,
                # height: Optional[int]=None,
                # padding: PaddingDimensions=(0, 1),
                # highlight: bool=False
            )
            print(panel)
        choices = []
        i = 0
        for ab_uuid in ed.addressees:
            add = ed.addressees[ab_uuid]
            if i < len(HOTKEYS):
                choices.append(
                    f'{HOTKEYS[i]}{add["last_name"].upper()} {add["first_name"]}, {add["city"]}|{ab_uuid}'
                )
                i += 1
            else:
                choices.append(
                    f'{add["last_name"].upper()} {add["first_name"]}, {add["city"]}|{ab_uuid}'
                )
        try:
            choice = menu(choices=choices, prompt="Select Addressee")
            myuuid = choices[choice].split("|")[-1]
            if os.path.isfile(TO):
                os.remove(TO)
        except TypeError:
            pass

    from_fields = [
        "title",
        "first_name",
        "last_name",
        "address_1",
        "address_2",
        "post_code",
        "city",
        "country",
    ]

    if os.path.isfile(TO):
        with open(TO) as f:
            von_frontmatter = frontmatter.load(f)
            try:
                myuuid = von_frontmatter["uuid"]
            except KeyError:
                von_frontmatter["uuid"] = myuuid
    else:
        # chose adressee or new
        von_frontmatter = frontmatter.Post(
            """# Addressee Information

Please confirm the following data

- Title - Herr, Frau, Professor Dr who or whatever or just leave it blank.
- First name
- Last name
- Address 1 - Usually street and number.
- Address 2 - Additional information. Often just blank.
- Post Code - Post code, wich is the [5 digit number if you are in
Germany](https://en.wikipedia.org/wiki/List_of_postal_codes_in_Germany).
- City - City name.
- Country - in English for international delivery sometimes the country code is found here: `DE Germany`
"""
        )

        von_frontmatter["document"] = "Addressee Information"
        von_frontmatter["uuid"] = myuuid
        if myuuid in ed.addressees:  # if in address book
            add = ed.addressees[myuuid]
            for von_item in from_fields:  # populate from address book
                try:
                    von_frontmatter[von_item] = ed.addressees[myuuid][von_item]
                except KeyError:
                    pass
        with open(TO, "w") as f:
            f.writelines(frontmatter.dumps(von_frontmatter))

    ed.cons.print(Markdown(von_frontmatter.content))

    ed.cons.print()
    for von_item in from_fields:
        try:
            ed.cons.rule(
                title=f'[green]{von_frontmatter[von_item]} [{COLOR}]❮ {von_item.replace("_", " ").capitalize()}',
                style=COLOR,
                align="left",
                characters="❮",
            )
        except KeyError as identfier:
            von_frontmatter[von_item] = ed.sess.prompt(
                default=von_item.replace("_", " ").capitalize()
            )
    continue_on_one = 1
    while continue_on_one == 1:
        continue_on_one = menu(["[1] OK", "[2] Edit Field"])
        if continue_on_one == 1:
            choices = []
            items = []
            i = 0
            for item in von_frontmatter.keys():
                if item in from_fields:
                    items.append(item)
                    choices.append(
                        f'{HOTKEYS[i]}{item.replace("_", " ").capitalize()}: {von_frontmatter[item]}'
                    )
                    i += 1
            try:
                choice = menu(prompt="Change field?", choices=choices)
                von_frontmatter[items[choice]] = ed.sess.prompt(
                    default=von_frontmatter[items[choice]]
                )
                ed.cons.rule(
                    title=f'[green]{von_frontmatter[items[choice]]} ❮ [{COLOR}]{items[choice].replace("_", " ").capitalize()}',
                    style=COLOR,
                    align="left",
                    characters="❮",
                )
            except TypeError:
                pass
        else:
            ed.cons.print()
            ed.cons.rule(
                title=f"[{COLOR}]Addressee Information checked and recorded in {str(TO)} ",
                style=COLOR,
                align="right",
            )
    with open(TO, "w") as f:
        f.writelines(frontmatter.dumps(von_frontmatter))
    if ed.addressees:
        myuuid = von_frontmatter["uuid"]
    else:
        ed.addressees = {}
    temp_dict = von_frontmatter.to_dict()
    del temp_dict["uuid"]
    del temp_dict["content"]
    del temp_dict["document"]
    ed.addressees.update({myuuid: temp_dict})
    with open(config_path, "w") as f:
        yaml.dump(ed.addressees, f)


@app.command()
def write():
    """Write the letter."""
    ed.cons.print(f"[{COLOR}]Checking Sender Information.")
    if not os.path.isfile(VON):
        sender()
    ed.cons.print(f"[{COLOR}]Checking Addressee Information.")
    if not os.path.isfile(TO):
        address()
    ed.cons.print(f"[bold]What is your favourite text editor right now?")
    ed.favourite = ed.sess.prompt(
        default=ed.favourite, completer=WordCompleter(EDITORS)
    )
    ed.sess = PromptSession(
        message="❯ ", wrap_lines=True, vi_mode=True, completer=ed.test_completer
    )
    current_dir = os.getcwd()
    if os.path.isfile(BODY):
        with open(BODY) as f:
            von_frontmatter = frontmatter.load(f)
    else:
        von_frontmatter = frontmatter.Post(
            """Letter about line will be printed in bold type.

This is the body of the letter.

> Replace with your own text.
"""
        )
        von_frontmatter["document"] = "Body Text"
        von_frontmatter["about"] = "About line of the letter."
        with open(BODY, "w") as f:
            f.writelines(frontmatter.dumps(von_frontmatter))
    # cons.print(Markdown(von_frontmatter.content))
    from_fields = ["about"]
    ed.cons.print()
    for von_item in from_fields:
        try:
            ed.cons.rule(
                title=f"[{COLOR}]❯ [bold green]{von_frontmatter[von_item]}",
                style=COLOR,
                align="left",
                characters="❮",
            )
        except KeyError as identfier:
            von_frontmatter[von_item] = ed.sess.prompt(
                default=von_item.replace("_", " ").capitalize()
            )
    continue_on_one = 1
    ed.cons.print(Markdown(von_frontmatter.content))
    while continue_on_one == 1:
        continue_on_one = menu(["[1] OK", "[2] Edit Field", "[3] Edit Letter Body"])
        if continue_on_one == 1:
            choices = []
            items = []
            for item in von_frontmatter.keys():
                items.append(item)
                choices.append(
                    f'{item.replace("_", " ").capitalize()}: {von_frontmatter[item]}'
                )
            choice = menu(prompt="Change field?", choices=choices)
            von_frontmatter[items[choice]] = ed.sess.prompt(
                default=von_frontmatter[items[choice]]
            )
            ed.cons.rule(
                title=f"[{COLOR}]❯ [green]{von_frontmatter[items[choice]]}",
                style=COLOR,
                align="left",
                characters="❮",
            )
        elif continue_on_one == 2:
            if ed.favourite in ["vi", "vim", "nvim"]:
                choices = [
                    "Deutsch|de_de",
                    "English (US)|en_us",
                    "English (GB)|en_gb",
                    "Deutsch (AU)|de_au",
                    "None",
                ]
                choice = menu(prompt="Spellang for checking?", choices=choices)
                if choice == 4:
                    spelllang = ""
                else:
                    spelllang = (
                        f'-c ":set spell spelllang={choices[choice].split("|")[-1]}" '
                    )
            else:
                spelllang = ""
            mtime = os.path.getmtime(BODY)
            os.system(f"{ed.favourite} {spelllang}{BODY}")
            if mtime == os.path.getmtime(BODY):
                ed.cons.print("\n[bold]No changes.")
            else:
                with open(BODY) as f:
                    von_frontmatter = frontmatter.load(f)
                ed.cons.print(Markdown(von_frontmatter.content))
            continue_on_one = 1
        else:
            ed.cons.print()
            with open(BODY, "w") as f:
                f.writelines(frontmatter.dumps(von_frontmatter))
            ed.cons.rule(
                title=f"[{COLOR}]Letter body text checked and recorded in {str(BODY)} ",
                style=COLOR,
                align="right",
            )
            if ed.last:
                ed.cons.print(
                    Panel(
                        renderable=str(ed.last),
                        style=COLOR,
                        title="configuration",
                        title_align="left",
                        subtitle="betterletter make --help",
                        subtitle_align="right",
                        border_style=COLOR,
                    )
                )


@app.command()
def make(
    image_file: str = typer.Option(
        None,
        "--image",
        "-i",
        help="A logo raster graphic file placed in the top left corner. Width: 20mm",
    ),
    body_font: str = typer.Option(
        None,
        "--bodyfont",
        help="Letter body font.",
    ),
    bold_font: str = typer.Option(
        None,
        "--boldfont",
        help="Bold font used in the about line.",
    ),
    logo_line1: str = typer.Option(
        None,
        "--logo1",
        "-1",
        help="Logo Line 1",
    ),
    logo_line1_font: str = typer.Option(
        None,
        "--logo1font",
        help="Logo Line 1 Font",
    ),
    logo_line2: str = typer.Option(
        None,
        "--logo2",
        "-2",
        help="Logo Line 2",
    ),
    logo_line2_font: str = typer.Option(
        None,
        "--logo2font",
        help="Logo Line 2 Font",
    ),
    logo_link: str = typer.Option(
        "https://betterletter.harmlos.info",
        "--link",
        "-l",
        help="Logo Link.",
    ),
):
    """Generate letter as PDF Document.

    Provide your own artwork:
    Place imgage file in working directory, use the `--image` option.

    Provide your own fonts:
    Place TTF Font files in working directory, use the following options to reference."""
    current_dir = os.getcwd()

    write()
    ed.cons.rule(
        title=f"[{COLOR}]betterletter make",
        style=COLOR,
        align="left",
    )
    if ed.last != {}:
        if not image_file:
            try:
                image_file = ed.last["image_file"]
            except KeyError:
                pass
        if not body_font:
            try:
                body_font = ed.last["body_font"]
            except KeyError:
                pass
        if not bold_font:
            try:
                bold_font = ed.last["bold_font"]
            except KeyError:
                pass
        if not logo_line1:
            try:
                logo_line1 = ed.last["logo_line1"]
            except KeyError:
                pass
        if not logo_line1_font:
            try:
                logo_line1_font = ed.last["logo_line1_font"]
            except KeyError:
                pass
        if not logo_line2:
            try:
                logo_line2 = ed.last["logo_line2"]
            except KeyError:
                pass
        if not logo_line2_font:
            try:
                logo_line2_font = ed.last["logo_line2_font"]
            except KeyError:
                pass
        if not logo_link:
            try:
                logo_link = ed.last["logo_link"]
            except KeyError:
                pass

    class PDF(FPDF, HTMLMixin):
        def header(self):
            pdf.set_left_margin(0)
            pdf.set_right_margin(0)
            # Rendering logo:
            if image_file:
                self.image(image_file, 20, 8, 20)
            # Setting font: helvetica bold 15
            if body_font:
                body = "body"
                pdf.add_font(body, fname=body_font, uni=True)
            else:
                body = "helvetica"
            self.set_font(body, "", 15)
            if not body_font and not logo_line1_font:
                self.set_font("Helvetica", "B", 15)
            if logo_line1_font:
                self.add_font("logo1", fname=logo_line1_font, uni=True)
                self.set_font("logo1", size=15)
            self.ln(5)
            # Moving cursor to the right:
            self.cell(w=40)
            # Printing title:
            if logo_line1:
                if image_file:
                    self.cell(txt=logo_line1)
                else:
                    self.set_x(x=0)
                    self.cell(w=210, txt=logo_line1, align="C")
            self.ln(6)
            self.cell(w=40)
            if logo_line2_font:
                self.add_font("logo2", fname=logo_line2_font, uni=True)
                self.set_font("logo2", size=6)
            else:
                self.set_font(body, "", size=6)
            if logo_line2:
                if image_file:
                    self.cell(txt=logo_line2, link=logo_link)
                else:
                    self.set_x(x=0)
                    self.cell(w=210, txt=logo_line2, link=logo_link, align="C")
            self.ln(12)
            # Text Margins
            self.set_left_margin(24)
            self.set_right_margin(24)
            if self.page_no() == 1:
                with open(VON) as f:
                    von_frontmatter = frontmatter.load(f)
                # cons.print(von_frontmatter["first_name"])
                self.set_font(body, "", 10)
                self.set_xy(x=120, y=51)
                self.cell(w=1, txt=von_frontmatter["title"])
                self.set_x(x=120)
                self.cell(
                    w=1,
                    txt=f'{von_frontmatter["first_name"]} {von_frontmatter["last_name"]}',
                )
                self.ln()
                self.set_x(x=120)
                self.cell(w=1, txt=von_frontmatter["address_1"])
                self.ln()
                self.set_x(x=120)
                self.cell(w=1, txt=von_frontmatter["address_2"])
                self.ln()
                self.set_x(x=120)
                self.cell(
                    w=1,
                    txt=von_frontmatter["post_code"] + " " + von_frontmatter["city"],
                )
                self.ln()
                self.set_x(x=120)
                self.cell(w=1, txt=von_frontmatter["country"])
                self.ln()
                self.set_x(x=120)
                self.cell(w=1, txt="Tel " + von_frontmatter["phone"])
                self.ln()
                self.set_x(x=120)
                self.cell(
                    w=1,
                    txt=von_frontmatter["email"],
                    link="mailto:" + von_frontmatter["email"],
                )
                self.ln()
                self.set_x(x=120)
                self.cell(
                    w=1,
                    txt=von_frontmatter["web_site"],
                    link="https://" + von_frontmatter["web_site"],
                )
                self.ln()
                with open(TO) as f:
                    an_frontmatter = frontmatter.load(f)
                self.set_font(body, "", 12)
                self.set_y(y=51)
                self.cell(w=1, txt=an_frontmatter["title"])
                self.ln()
                self.cell(
                    w=1,
                    txt=f'{an_frontmatter["first_name"]} {an_frontmatter["last_name"]}',
                )
                self.ln()
                # pdf.set_x(x=10)
                self.cell(w=1, txt=an_frontmatter["address_1"])
                self.ln()
                # pdf.set_x(x=10)
                self.cell(w=1, txt=an_frontmatter["address_2"])
                self.ln()
                # pdf.set_x(x=10)
                self.cell(
                    w=1, txt=an_frontmatter["post_code"] + " " + an_frontmatter["city"]
                )
                self.ln()
                self.ln()
                self.cell(w=1, txt=an_frontmatter["country"])
                self.ln()
                self.set_font("helvetica", size=12)
                text = von_frontmatter["city"]
                text = text.strip() + ", " + datetime.now().now().strftime("%d.%m.%Y")
                self.ln()
                self.set_x(x=120)
                self.cell(txt=text)

        def footer(self):
            # Position cursor at 1.5 cm from bottom:
            self.set_y(-15)
            # Setting font: helvetica italic 8
            self.set_font("helvetica", "I", 8)
            # Printing page number:
            self.cell(
                0,
                10,
                f"a betterletter -  p {self.page_no()}/{{nb}}",
                0,
                0,
                "C",
                link="https://betterletter.harmlos.info",
            )
            # Add folding marks
            self.line(x1=0, y1=87, x2=6, y2=87)
            self.line(x1=0, y1=148.5, x2=9, y2=148.5)
            self.line(x1=0, y1=192, x2=6, y2=192)

    with ed.cons.status(str(PDF_FILE), spinner="pong"):
        pdf = PDF()
        # count pages - see footer()
        pdf.alias_nb_pages()
        # add first page - auto page break is enabled by default
        pdf.add_page()
        # set font
        with open(BODY) as f:
            body_frontmatter = frontmatter.load(f)
            if body_font:
                pdf.set_font("body", "", 12)
            else:
                pdf.set_font("helvetica", "B", 12)
            if bold_font:
                pdf.add_font("bold", fname=bold_font, uni=True)
                pdf.set_font("bold", "", 12)
            line_height = pdf.font_size * 1.2
            pdf.set_y(y=91)
            pdf.write(txt=body_frontmatter["about"])
            if body_font:
                pdf.set_font("body", "", 10)
            else:
                pdf.set_font("helvetica", "", 10)
            pdf.ln()
            html_body = markdown.markdown(
                body_frontmatter.content, extensions=["toc", "extra"]
            )
            pdf.write_html(text=html_body)
        with open(VON) as f:
            von_frontmatter = frontmatter.load(f)
            pdf.ln()
            pdf.ln()
            pdf.ln()
            pdf.write(txt="______________________________")
            pdf.ln(7.5)
            if body_font:
                pdf.set_font("body", "", 8)
            else:
                pdf.set_font("helvetica", "", 8)
            pdf.write(
                txt=f'{von_frontmatter["title"]} {von_frontmatter["first_name"]} {von_frontmatter["last_name"]}'
            )
        pdf.output(PDF_FILE)
        ed.cons.print(f"[green]{str(PDF_FILE)} [{COLOR}]ready in {current_dir}")
        if image_file:
            ed.last.update({"image_file": image_file})
        if body_font:
            ed.last.update({"body_font": body_font})
        if bold_font:
            ed.last.update({"bold_font": bold_font})
        if logo_line1:
            ed.last.update({"logo_line1": logo_line1})
        if logo_line1_font:
            ed.last.update({"logo_line1_font": logo_line1_font})
        if logo_line2:
            ed.last.update({"logo_line2": logo_line2})
        if logo_line2_font:
            ed.last.update({"logo_line2_font": logo_line2_font})
        if logo_link:
            ed.last.update({"logo_link": logo_link})
        if ed.last != {}:
            with open("betterletter_make.yml", "w") as f:
                yaml.dump(ed.last, f)


def menu(choices, prompt=""):
    """Ask for user interaction."""
    main_menu_title = prompt
    main_menu_items = choices
    main_menu_cursor = "👉 "
    main_menu_cursor_style = ("fg_red", "bold")
    main_menu = TerminalMenu(
        menu_entries=main_menu_items,
        title=main_menu_title,
        menu_cursor=main_menu_cursor,
        menu_cursor_style=main_menu_cursor_style,
        cycle_cursor=True,
        clear_screen=False,
        show_search_hint=False,
        accept_keys=("enter", "alt-d", "ctrl-i", " "),
    )
    return main_menu.show()


if __name__ == "__main__":
    app()

Functions

def address(address_book: bool = <typer.models.OptionInfo object>)

Addressee information.

Expand source code
@app.command()
def address(
    address_book: bool = typer.Option(
        False,
        "--book",
        "-b",
        help="Address Book.",
    ),
):
    """Addressee information."""
    current_dir = os.getcwd()
    myuuid = str(uuid.uuid4())
    # print(myuuid)
    if address_book:
        for a_uuid in ed.addressees:
            add = ed.addressees[a_uuid]
            panel = Panel(
                renderable=f'[bold]{add["last_name"].upper()} {add["first_name"]}[/] {add["title"]}'.strip(),
                title=f'{add["address_1"]} [bold]{add["city"].strip()}',
                title_align="left",
                subtitle=f'{add["address_2"]} {add["city"]}'.strip(),
                subtitle_align="right",
                expand=False,
                style=COLOR,
                border_style=COLOR,
                # width: Optional[int]=None,
                # height: Optional[int]=None,
                # padding: PaddingDimensions=(0, 1),
                # highlight: bool=False
            )
            print(panel)
        choices = []
        i = 0
        for ab_uuid in ed.addressees:
            add = ed.addressees[ab_uuid]
            if i < len(HOTKEYS):
                choices.append(
                    f'{HOTKEYS[i]}{add["last_name"].upper()} {add["first_name"]}, {add["city"]}|{ab_uuid}'
                )
                i += 1
            else:
                choices.append(
                    f'{add["last_name"].upper()} {add["first_name"]}, {add["city"]}|{ab_uuid}'
                )
        try:
            choice = menu(choices=choices, prompt="Select Addressee")
            myuuid = choices[choice].split("|")[-1]
            if os.path.isfile(TO):
                os.remove(TO)
        except TypeError:
            pass

    from_fields = [
        "title",
        "first_name",
        "last_name",
        "address_1",
        "address_2",
        "post_code",
        "city",
        "country",
    ]

    if os.path.isfile(TO):
        with open(TO) as f:
            von_frontmatter = frontmatter.load(f)
            try:
                myuuid = von_frontmatter["uuid"]
            except KeyError:
                von_frontmatter["uuid"] = myuuid
    else:
        # chose adressee or new
        von_frontmatter = frontmatter.Post(
            """# Addressee Information

Please confirm the following data

- Title - Herr, Frau, Professor Dr who or whatever or just leave it blank.
- First name
- Last name
- Address 1 - Usually street and number.
- Address 2 - Additional information. Often just blank.
- Post Code - Post code, wich is the [5 digit number if you are in
Germany](https://en.wikipedia.org/wiki/List_of_postal_codes_in_Germany).
- City - City name.
- Country - in English for international delivery sometimes the country code is found here: `DE Germany`
"""
        )

        von_frontmatter["document"] = "Addressee Information"
        von_frontmatter["uuid"] = myuuid
        if myuuid in ed.addressees:  # if in address book
            add = ed.addressees[myuuid]
            for von_item in from_fields:  # populate from address book
                try:
                    von_frontmatter[von_item] = ed.addressees[myuuid][von_item]
                except KeyError:
                    pass
        with open(TO, "w") as f:
            f.writelines(frontmatter.dumps(von_frontmatter))

    ed.cons.print(Markdown(von_frontmatter.content))

    ed.cons.print()
    for von_item in from_fields:
        try:
            ed.cons.rule(
                title=f'[green]{von_frontmatter[von_item]} [{COLOR}]❮ {von_item.replace("_", " ").capitalize()}',
                style=COLOR,
                align="left",
                characters="❮",
            )
        except KeyError as identfier:
            von_frontmatter[von_item] = ed.sess.prompt(
                default=von_item.replace("_", " ").capitalize()
            )
    continue_on_one = 1
    while continue_on_one == 1:
        continue_on_one = menu(["[1] OK", "[2] Edit Field"])
        if continue_on_one == 1:
            choices = []
            items = []
            i = 0
            for item in von_frontmatter.keys():
                if item in from_fields:
                    items.append(item)
                    choices.append(
                        f'{HOTKEYS[i]}{item.replace("_", " ").capitalize()}: {von_frontmatter[item]}'
                    )
                    i += 1
            try:
                choice = menu(prompt="Change field?", choices=choices)
                von_frontmatter[items[choice]] = ed.sess.prompt(
                    default=von_frontmatter[items[choice]]
                )
                ed.cons.rule(
                    title=f'[green]{von_frontmatter[items[choice]]} ❮ [{COLOR}]{items[choice].replace("_", " ").capitalize()}',
                    style=COLOR,
                    align="left",
                    characters="❮",
                )
            except TypeError:
                pass
        else:
            ed.cons.print()
            ed.cons.rule(
                title=f"[{COLOR}]Addressee Information checked and recorded in {str(TO)} ",
                style=COLOR,
                align="right",
            )
    with open(TO, "w") as f:
        f.writelines(frontmatter.dumps(von_frontmatter))
    if ed.addressees:
        myuuid = von_frontmatter["uuid"]
    else:
        ed.addressees = {}
    temp_dict = von_frontmatter.to_dict()
    del temp_dict["uuid"]
    del temp_dict["content"]
    del temp_dict["document"]
    ed.addressees.update({myuuid: temp_dict})
    with open(config_path, "w") as f:
        yaml.dump(ed.addressees, f)
def callback(ctx: typer.models.Context)

This is the callback function invoked when the app is run without any commands.

By default this just shows the –help. Here however the otion to run the app without any commands has been enabled.

Expand source code
@app.callback(invoke_without_command=True)
def callback(ctx: typer.Context):
    """This is the callback function invoked when the app is run without any commands.

    By default this just shows the --help. Here however the otion to run the app without any commands has been enabled.
    """
    ed.cons.rule(title="better letter", align="right", style=COLOR)
    sys.stdout.write("\x1b]2;" + "Betterletter" + "\x07")
    if ctx.invoked_subcommand is None:  # invoked without commands.
        ed.cons.print(ctx.get_help())
def make(image_file: str = <typer.models.OptionInfo object>, body_font: str = <typer.models.OptionInfo object>, bold_font: str = <typer.models.OptionInfo object>, logo_line1: str = <typer.models.OptionInfo object>, logo_line1_font: str = <typer.models.OptionInfo object>, logo_line2: str = <typer.models.OptionInfo object>, logo_line2_font: str = <typer.models.OptionInfo object>, logo_link: str = <typer.models.OptionInfo object>)

Generate letter as PDF Document.

Provide your own artwork: Place imgage file in working directory, use the --image option.

Provide your own fonts: Place TTF Font files in working directory, use the following options to reference.

Expand source code
@app.command()
def make(
    image_file: str = typer.Option(
        None,
        "--image",
        "-i",
        help="A logo raster graphic file placed in the top left corner. Width: 20mm",
    ),
    body_font: str = typer.Option(
        None,
        "--bodyfont",
        help="Letter body font.",
    ),
    bold_font: str = typer.Option(
        None,
        "--boldfont",
        help="Bold font used in the about line.",
    ),
    logo_line1: str = typer.Option(
        None,
        "--logo1",
        "-1",
        help="Logo Line 1",
    ),
    logo_line1_font: str = typer.Option(
        None,
        "--logo1font",
        help="Logo Line 1 Font",
    ),
    logo_line2: str = typer.Option(
        None,
        "--logo2",
        "-2",
        help="Logo Line 2",
    ),
    logo_line2_font: str = typer.Option(
        None,
        "--logo2font",
        help="Logo Line 2 Font",
    ),
    logo_link: str = typer.Option(
        "https://betterletter.harmlos.info",
        "--link",
        "-l",
        help="Logo Link.",
    ),
):
    """Generate letter as PDF Document.

    Provide your own artwork:
    Place imgage file in working directory, use the `--image` option.

    Provide your own fonts:
    Place TTF Font files in working directory, use the following options to reference."""
    current_dir = os.getcwd()

    write()
    ed.cons.rule(
        title=f"[{COLOR}]betterletter make",
        style=COLOR,
        align="left",
    )
    if ed.last != {}:
        if not image_file:
            try:
                image_file = ed.last["image_file"]
            except KeyError:
                pass
        if not body_font:
            try:
                body_font = ed.last["body_font"]
            except KeyError:
                pass
        if not bold_font:
            try:
                bold_font = ed.last["bold_font"]
            except KeyError:
                pass
        if not logo_line1:
            try:
                logo_line1 = ed.last["logo_line1"]
            except KeyError:
                pass
        if not logo_line1_font:
            try:
                logo_line1_font = ed.last["logo_line1_font"]
            except KeyError:
                pass
        if not logo_line2:
            try:
                logo_line2 = ed.last["logo_line2"]
            except KeyError:
                pass
        if not logo_line2_font:
            try:
                logo_line2_font = ed.last["logo_line2_font"]
            except KeyError:
                pass
        if not logo_link:
            try:
                logo_link = ed.last["logo_link"]
            except KeyError:
                pass

    class PDF(FPDF, HTMLMixin):
        def header(self):
            pdf.set_left_margin(0)
            pdf.set_right_margin(0)
            # Rendering logo:
            if image_file:
                self.image(image_file, 20, 8, 20)
            # Setting font: helvetica bold 15
            if body_font:
                body = "body"
                pdf.add_font(body, fname=body_font, uni=True)
            else:
                body = "helvetica"
            self.set_font(body, "", 15)
            if not body_font and not logo_line1_font:
                self.set_font("Helvetica", "B", 15)
            if logo_line1_font:
                self.add_font("logo1", fname=logo_line1_font, uni=True)
                self.set_font("logo1", size=15)
            self.ln(5)
            # Moving cursor to the right:
            self.cell(w=40)
            # Printing title:
            if logo_line1:
                if image_file:
                    self.cell(txt=logo_line1)
                else:
                    self.set_x(x=0)
                    self.cell(w=210, txt=logo_line1, align="C")
            self.ln(6)
            self.cell(w=40)
            if logo_line2_font:
                self.add_font("logo2", fname=logo_line2_font, uni=True)
                self.set_font("logo2", size=6)
            else:
                self.set_font(body, "", size=6)
            if logo_line2:
                if image_file:
                    self.cell(txt=logo_line2, link=logo_link)
                else:
                    self.set_x(x=0)
                    self.cell(w=210, txt=logo_line2, link=logo_link, align="C")
            self.ln(12)
            # Text Margins
            self.set_left_margin(24)
            self.set_right_margin(24)
            if self.page_no() == 1:
                with open(VON) as f:
                    von_frontmatter = frontmatter.load(f)
                # cons.print(von_frontmatter["first_name"])
                self.set_font(body, "", 10)
                self.set_xy(x=120, y=51)
                self.cell(w=1, txt=von_frontmatter["title"])
                self.set_x(x=120)
                self.cell(
                    w=1,
                    txt=f'{von_frontmatter["first_name"]} {von_frontmatter["last_name"]}',
                )
                self.ln()
                self.set_x(x=120)
                self.cell(w=1, txt=von_frontmatter["address_1"])
                self.ln()
                self.set_x(x=120)
                self.cell(w=1, txt=von_frontmatter["address_2"])
                self.ln()
                self.set_x(x=120)
                self.cell(
                    w=1,
                    txt=von_frontmatter["post_code"] + " " + von_frontmatter["city"],
                )
                self.ln()
                self.set_x(x=120)
                self.cell(w=1, txt=von_frontmatter["country"])
                self.ln()
                self.set_x(x=120)
                self.cell(w=1, txt="Tel " + von_frontmatter["phone"])
                self.ln()
                self.set_x(x=120)
                self.cell(
                    w=1,
                    txt=von_frontmatter["email"],
                    link="mailto:" + von_frontmatter["email"],
                )
                self.ln()
                self.set_x(x=120)
                self.cell(
                    w=1,
                    txt=von_frontmatter["web_site"],
                    link="https://" + von_frontmatter["web_site"],
                )
                self.ln()
                with open(TO) as f:
                    an_frontmatter = frontmatter.load(f)
                self.set_font(body, "", 12)
                self.set_y(y=51)
                self.cell(w=1, txt=an_frontmatter["title"])
                self.ln()
                self.cell(
                    w=1,
                    txt=f'{an_frontmatter["first_name"]} {an_frontmatter["last_name"]}',
                )
                self.ln()
                # pdf.set_x(x=10)
                self.cell(w=1, txt=an_frontmatter["address_1"])
                self.ln()
                # pdf.set_x(x=10)
                self.cell(w=1, txt=an_frontmatter["address_2"])
                self.ln()
                # pdf.set_x(x=10)
                self.cell(
                    w=1, txt=an_frontmatter["post_code"] + " " + an_frontmatter["city"]
                )
                self.ln()
                self.ln()
                self.cell(w=1, txt=an_frontmatter["country"])
                self.ln()
                self.set_font("helvetica", size=12)
                text = von_frontmatter["city"]
                text = text.strip() + ", " + datetime.now().now().strftime("%d.%m.%Y")
                self.ln()
                self.set_x(x=120)
                self.cell(txt=text)

        def footer(self):
            # Position cursor at 1.5 cm from bottom:
            self.set_y(-15)
            # Setting font: helvetica italic 8
            self.set_font("helvetica", "I", 8)
            # Printing page number:
            self.cell(
                0,
                10,
                f"a betterletter -  p {self.page_no()}/{{nb}}",
                0,
                0,
                "C",
                link="https://betterletter.harmlos.info",
            )
            # Add folding marks
            self.line(x1=0, y1=87, x2=6, y2=87)
            self.line(x1=0, y1=148.5, x2=9, y2=148.5)
            self.line(x1=0, y1=192, x2=6, y2=192)

    with ed.cons.status(str(PDF_FILE), spinner="pong"):
        pdf = PDF()
        # count pages - see footer()
        pdf.alias_nb_pages()
        # add first page - auto page break is enabled by default
        pdf.add_page()
        # set font
        with open(BODY) as f:
            body_frontmatter = frontmatter.load(f)
            if body_font:
                pdf.set_font("body", "", 12)
            else:
                pdf.set_font("helvetica", "B", 12)
            if bold_font:
                pdf.add_font("bold", fname=bold_font, uni=True)
                pdf.set_font("bold", "", 12)
            line_height = pdf.font_size * 1.2
            pdf.set_y(y=91)
            pdf.write(txt=body_frontmatter["about"])
            if body_font:
                pdf.set_font("body", "", 10)
            else:
                pdf.set_font("helvetica", "", 10)
            pdf.ln()
            html_body = markdown.markdown(
                body_frontmatter.content, extensions=["toc", "extra"]
            )
            pdf.write_html(text=html_body)
        with open(VON) as f:
            von_frontmatter = frontmatter.load(f)
            pdf.ln()
            pdf.ln()
            pdf.ln()
            pdf.write(txt="______________________________")
            pdf.ln(7.5)
            if body_font:
                pdf.set_font("body", "", 8)
            else:
                pdf.set_font("helvetica", "", 8)
            pdf.write(
                txt=f'{von_frontmatter["title"]} {von_frontmatter["first_name"]} {von_frontmatter["last_name"]}'
            )
        pdf.output(PDF_FILE)
        ed.cons.print(f"[green]{str(PDF_FILE)} [{COLOR}]ready in {current_dir}")
        if image_file:
            ed.last.update({"image_file": image_file})
        if body_font:
            ed.last.update({"body_font": body_font})
        if bold_font:
            ed.last.update({"bold_font": bold_font})
        if logo_line1:
            ed.last.update({"logo_line1": logo_line1})
        if logo_line1_font:
            ed.last.update({"logo_line1_font": logo_line1_font})
        if logo_line2:
            ed.last.update({"logo_line2": logo_line2})
        if logo_line2_font:
            ed.last.update({"logo_line2_font": logo_line2_font})
        if logo_link:
            ed.last.update({"logo_link": logo_link})
        if ed.last != {}:
            with open("betterletter_make.yml", "w") as f:
                yaml.dump(ed.last, f)
def menu(choices, prompt='')

Ask for user interaction.

Expand source code
def menu(choices, prompt=""):
    """Ask for user interaction."""
    main_menu_title = prompt
    main_menu_items = choices
    main_menu_cursor = "👉 "
    main_menu_cursor_style = ("fg_red", "bold")
    main_menu = TerminalMenu(
        menu_entries=main_menu_items,
        title=main_menu_title,
        menu_cursor=main_menu_cursor,
        menu_cursor_style=main_menu_cursor_style,
        cycle_cursor=True,
        clear_screen=False,
        show_search_hint=False,
        accept_keys=("enter", "alt-d", "ctrl-i", " "),
    )
    return main_menu.show()
def sender()

Work with the sender information

Expand source code
@app.command()
def sender():
    """Work with the sender information"""
    current_dir = os.getcwd()
    # try:
    #     dirs_from_conf = conf["dirs"]
    #     if current_dir not in dirs_from_conf:

    #         cons.print(f'{current_dir} is not in congfiguration. Would you like to add it?')

    # except KeyError:
    #     cons.print(f'Setting up {current_dir} as a working directory.')
    #     dirs_from_conf = [current_dir]
    #     with open(config_path, 'w') as f: # safe
    #         yaml.dump(conf, f)
    if os.path.isfile(VON):
        with open(VON) as f:
            von_frontmatter = frontmatter.load(f)
    else:
        von_frontmatter = frontmatter.Post(
            """# Sender Information

Please confirm the following data

- Title - Herr, Frau, Professor Dr who or whatever or just leave it blank.
- First name
- Last name
- Address 1 - Usually street and number.
- Address 2 - Additional information. Often just blank.
- Address 3 - Post code, wich is the [5 digit number if you are in
Germany](https://en.wikipedia.org/wiki/List_of_postal_codes_in_Germany) and city.
Sometimes prepended with a country code, i.e. `DE` for Germany.
- Country - in English for international delivery sometimes the country code is found here: `DE Germany`
"""
        )
        von_frontmatter["document"] = "Sender Information"
        with open(VON, "w") as f:
            f.writelines(frontmatter.dumps(von_frontmatter))
    ed.cons.print(Markdown(von_frontmatter.content))
    from_fields = [
        "title",
        "first_name",
        "last_name",
        "address_1",
        "address_2",
        "post_code",
        "city",
        "country",
        "phone",
        "email",
        "web_site",
    ]
    ed.cons.print()
    for von_item in from_fields:
        try:
            ed.cons.rule(
                title=f'[green]{von_frontmatter[von_item]} [{COLOR}]❮ {von_item.replace("_", " ").capitalize()}',
                style=COLOR,
                align="left",
                characters="❮",
            )
        except KeyError as identfier:
            von_frontmatter[von_item] = ed.sess.prompt(
                default=von_item.replace("_", " ").capitalize()
            )
    continue_on_one = 1
    while continue_on_one == 1:
        continue_on_one = menu(["[1] OK", "[2] Edit Field"])
        if continue_on_one == 1:
            choices = []
            items = []
            i = 0
            for item in von_frontmatter.keys():
                if item in from_fields:
                    items.append(item)
                    choices.append(
                        f'{HOTKEYS[i]}{item.replace("_", " ").capitalize()}: {von_frontmatter[item]}'
                    )
                i += 1
            try:
                choice = menu(prompt="Field?", choices=choices)

                von_frontmatter[items[choice]] = ed.sess.prompt(
                    default=von_frontmatter[items[choice]]
                )
                ed.cons.rule(
                    title=f'[green]{von_frontmatter[items[choice]]} [{COLOR}]❮ {items[choice].replace("_", " ").capitalize()}',
                    style=COLOR,
                    align="left",
                    characters="❮",
                )
            except TypeError:
                pass
        else:
            ed.cons.print()
            ed.cons.rule(
                title=f"[{COLOR}]Sender Information checked and recorded in {str(VON)} ",
                style=COLOR,
                align="right",
            )

    with open(VON, "w") as f:
        f.writelines(frontmatter.dumps(von_frontmatter))
def write()

Write the letter.

Expand source code
@app.command()
def write():
    """Write the letter."""
    ed.cons.print(f"[{COLOR}]Checking Sender Information.")
    if not os.path.isfile(VON):
        sender()
    ed.cons.print(f"[{COLOR}]Checking Addressee Information.")
    if not os.path.isfile(TO):
        address()
    ed.cons.print(f"[bold]What is your favourite text editor right now?")
    ed.favourite = ed.sess.prompt(
        default=ed.favourite, completer=WordCompleter(EDITORS)
    )
    ed.sess = PromptSession(
        message="❯ ", wrap_lines=True, vi_mode=True, completer=ed.test_completer
    )
    current_dir = os.getcwd()
    if os.path.isfile(BODY):
        with open(BODY) as f:
            von_frontmatter = frontmatter.load(f)
    else:
        von_frontmatter = frontmatter.Post(
            """Letter about line will be printed in bold type.

This is the body of the letter.

> Replace with your own text.
"""
        )
        von_frontmatter["document"] = "Body Text"
        von_frontmatter["about"] = "About line of the letter."
        with open(BODY, "w") as f:
            f.writelines(frontmatter.dumps(von_frontmatter))
    # cons.print(Markdown(von_frontmatter.content))
    from_fields = ["about"]
    ed.cons.print()
    for von_item in from_fields:
        try:
            ed.cons.rule(
                title=f"[{COLOR}]❯ [bold green]{von_frontmatter[von_item]}",
                style=COLOR,
                align="left",
                characters="❮",
            )
        except KeyError as identfier:
            von_frontmatter[von_item] = ed.sess.prompt(
                default=von_item.replace("_", " ").capitalize()
            )
    continue_on_one = 1
    ed.cons.print(Markdown(von_frontmatter.content))
    while continue_on_one == 1:
        continue_on_one = menu(["[1] OK", "[2] Edit Field", "[3] Edit Letter Body"])
        if continue_on_one == 1:
            choices = []
            items = []
            for item in von_frontmatter.keys():
                items.append(item)
                choices.append(
                    f'{item.replace("_", " ").capitalize()}: {von_frontmatter[item]}'
                )
            choice = menu(prompt="Change field?", choices=choices)
            von_frontmatter[items[choice]] = ed.sess.prompt(
                default=von_frontmatter[items[choice]]
            )
            ed.cons.rule(
                title=f"[{COLOR}]❯ [green]{von_frontmatter[items[choice]]}",
                style=COLOR,
                align="left",
                characters="❮",
            )
        elif continue_on_one == 2:
            if ed.favourite in ["vi", "vim", "nvim"]:
                choices = [
                    "Deutsch|de_de",
                    "English (US)|en_us",
                    "English (GB)|en_gb",
                    "Deutsch (AU)|de_au",
                    "None",
                ]
                choice = menu(prompt="Spellang for checking?", choices=choices)
                if choice == 4:
                    spelllang = ""
                else:
                    spelllang = (
                        f'-c ":set spell spelllang={choices[choice].split("|")[-1]}" '
                    )
            else:
                spelllang = ""
            mtime = os.path.getmtime(BODY)
            os.system(f"{ed.favourite} {spelllang}{BODY}")
            if mtime == os.path.getmtime(BODY):
                ed.cons.print("\n[bold]No changes.")
            else:
                with open(BODY) as f:
                    von_frontmatter = frontmatter.load(f)
                ed.cons.print(Markdown(von_frontmatter.content))
            continue_on_one = 1
        else:
            ed.cons.print()
            with open(BODY, "w") as f:
                f.writelines(frontmatter.dumps(von_frontmatter))
            ed.cons.rule(
                title=f"[{COLOR}]Letter body text checked and recorded in {str(BODY)} ",
                style=COLOR,
                align="right",
            )
            if ed.last:
                ed.cons.print(
                    Panel(
                        renderable=str(ed.last),
                        style=COLOR,
                        title="configuration",
                        title_align="left",
                        subtitle="betterletter make --help",
                        subtitle_align="right",
                        border_style=COLOR,
                    )
                )

Classes

class Editor

Configuration Class.

Expand source code
class Editor:
    """Configuration Class."""

    # Generating Completions
    completer_list = []

    if os.path.isfile(VON):
        with open(VON) as f:
            von_frontmatter = frontmatter.load(f)
            temp_dict = von_frontmatter.to_dict()
            del temp_dict["content"]
            del temp_dict["document"]
            for key in temp_dict:
                words = temp_dict[key].split()
                if words != []:
                    completer_list.extend(words)

    if os.path.isfile(TO):
        with open(TO) as f:
            von_frontmatter = frontmatter.load(f)
            temp_dict = von_frontmatter.to_dict()
            del temp_dict["content"]
            del temp_dict["document"]
            del temp_dict["uuid"]
            for key in temp_dict:
                words = temp_dict[key].split()
                if words != []:
                    completer_list.extend(words)

    if os.path.isfile(BODY):
        with open(BODY) as f:
            von_frontmatter = frontmatter.load(f)
            temp_dict = von_frontmatter.to_dict()
            del temp_dict["document"]
            for line in temp_dict:
                words = temp_dict[line].split()
                if words != []:
                    completer_list.extend(words)

    # removing duplicate words
    completer_list = list(dict.fromkeys(completer_list))
    test_completer = WordCompleter(sorted(completer_list))
    favourite = "vi"
    cons = Console()
    sess = PromptSession(
        message="❯ ", wrap_lines=True, vi_mode=True, completer=test_completer
    )
    last = {}
    if os.path.isfile("betterletter_make.yml"):
        with open("betterletter_make.yml") as f:
            last = yaml.load(f, Loader=yaml.FullLoader)
    adressees = {}
    with open(config_path) as f:
        addressees = yaml.load(f, Loader=yaml.FullLoader)

Class variables

var addressees
var adressees
var completer_list
var cons
var f
var favourite
var key
var last
var line
var sess
var temp_dict
var test_completer
var von_frontmatter
var words