Jhonatan da Silva

Jhonatan da Silva

How to make a Python script for your notes

By the end of the tutorial, you'll have your own python script to manage your zettels, something like this

python zettels.py new literature Literature zettel created at zettels/literature/1623412276.md

python zettels.py new literature Literature zettel created at zettels/literature/1623412278.md

python zettels.py list -a 10


File: zettels/literature/1623412276.md

number:1a
date: '2021-06-11'
tags: #tag1 #tag2

Line of thought 1a
### References
- Ref 1

You'll be able to create your own

  1. Fleeting Notes
  2. Literature Notes
  3. Permanent Notes
  4. Queue Notes

If you are not familiar with Zettelkasten, I suggest that you read Book Review - How to take smart notes

Typer

In order to do that, you can use typer to make a cli interface for your program. This is a basic typer app + the configuration for where your notes and templates will be.

import typer
from pathlib import Path

app = typer.Typer()

templates_path = Path("./zettels_template")
notes_path = Path("./zettels")

if __name__ == "__main__":
    app()

Now, we need to use the decorator, @app.command() to tell typer which commands we want to make, in our case, we want a way to create notes, and a way to list them. So we'll have new and list commands.

@app.command()
def new():
  typer.echo("new")

@app.command()
def list():
  typer.echo("list")

If you test this now, you'll see

python zettels.py new new

python zettels.py list list

Now we need the actual logic for the new script

@app.command()
def new(
    ztype: str = typer.Argument("l", help=helper_notes),
):
    ztype = set_note_type(ztype)
    timestamp = int(time.time())
    template_path = Path(templates_path, f"{ztype}.md")
    path2note = Path(notes_path, ztype, f"{timestamp}.md")
    with open(template_path, "r") as template:
        with open(path2note, "w") as note:
            for line in template:
                if "%date%" in line:
                    date2site = datetime.datetime.now().strftime("%Y-%m-%d")
                    note.write(f"date: '{date2site}'\n")
                else:
                    note.write(line)

    print(f"[*] {ztype} zettel created at {path2note}")
    # os.system(f"nvim {path2note}")

The logic is that we get the path for our template (that we haven't created yet), and the path to where we want to write our note. Then we open the template and write the contents to the note path, replacing something with what we want, like changing %date% for a timestamp.

An optional thing to do (it's commented) is to open your code editor, I like neovim, so I use it like this

os.system(f"nvim {path2note}")

But you could change this to code if you use vscode. You could also use subprocess.run instead of os.system.

Now we need the actual template, I myself use something like this, you need to add this to the zettel_template/literature.md. Or the actual path you've defined in the script.

---
number: 
title: ""
date: %date%
tags:
  - #tag1
  - #tag2
---

Description of the LITERATURE NOTE goes here

### References 
- 1    

You can create something like this for every type of note, like permanent, fleeting and queue. For queue in specific, I like to have something like project, done, timespent and so on.

Listing all the notes

Now for the listing notes side of the script

@app.command()
def list(
    ztype: str = typer.Argument("l", help=helper_notes),
    after: int = typer.Option(
        1, "-a", help="How many lines after do you want to show"
    ),
):
    slip_box_path = Path(notes_path, ztype)
    command = ""
    try:
        querystring = "^number: ?[0-9]"
        output = subprocess.check_output(
            ["rg", "-l", querystring, slip_box_path], encoding="utf-8"
        )
    except Exception:
        typer.echo("No notes")
    else:
        zettels = output.splitlines()
        for zettel in zettels:
            command = f"bat {zettel}"
            if after:
                command = f"{command} -r 0:{after}"
            os.system(command)

The idea here is more complex than creating the note, we use regex.

You need to have rg installed, if you are on linux it's just

sudo apt-get install ripgrep

Then we use rg to search for words inside the files we in the notes_path. The regex ^number: ?[0-9] means find everything start starts with number: + an optional blank space, then numbers from 0-9 and the rest doesn't matter.

So when you create your note with the template, you'll add 1, 1a, 1b, 1a1, 2, 2a, 2a1, 2a1a and so on, and this regex will find every file that matches this. You could also just list all files in the folder, but with this approach, you can start implementing your own scripts, like filtering only the ones that have 2, like ^number:2 that means, find every match that starts with number:2 so it'll find 2, 2a, 2a1 and so on.

And in the end, if you want a pretty print, I recommend using bat, in some computers it is installed as batcat, so you'll need to change the command to

command = f"batcat {zettel}"

And that's pretty much it. I also go over this in a more hand on approach in my 1 year box set of tutorials. More specifically, how to create this script and debug in a step-by-step manner. Thanks for the time!

Backlinks: