Instantly share code, notes, and snippets.
Created
August 29, 2023 17:41
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save abul4fia/796a3d4953fe6bef12cf0475e89a64f9 to your computer and use it in GitHub Desktop.
Process logseq journals to include navigation links
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
This little script is meant to be run from a graph folder, so that the folder "journals" | |
is a subfolder of the current folder. | |
It will iterate over all the files in the "journals" folder whose name matches the pattern | |
YYYY_MM_DD.md and for each one it will add or update the navigation line in the file. | |
The navigation line is the first item in the file and it will contain a link to the | |
previous and next dates in the journal with respect to the date of the journal file. | |
You can edit the global variables at the beginning of the script to change the format. | |
The script can be run several times. The fist time it will insert the navigation line | |
in all journal files. Subsequent runs will update the navigation line only if neccessary | |
(new dates have been inserted or removed) | |
The last journal file does not have a "next date", so the navigation line will contain | |
"No next date" in that case. Once the script has be run for the first time, it is more | |
convenient to use a template that includes the navigation line, and uses the logseq | |
variables <% yesterday %> and <% tomorrow %> so that each subsequent journal created | |
has the navigation line already included. | |
""" | |
# Global variables that affect the result | |
# The format of an internal link to a date | |
date_format = "%Y-%m-%d %A" # Example: "2021-01-01 Friday" | |
# The format of the navigation link (first item in each journal file) | |
navigation_format = "- **Navigation**: Previous entry: [[{previous_date}]], Next entry: [[{next_date}]]\n" | |
# String for the case in which there is no previous or next date | |
no_previous_date = "No previous date" | |
no_next_date = "No next date" | |
import pathlib | |
from datetime import datetime, timedelta | |
class DateChain: | |
"""This class stores a set of dates and allows to get the next | |
and previous date in the set for a given date""" | |
def __init__(self): | |
self.dates = set() | |
self.minimum = None | |
self.maximum = None | |
def add(self, date): | |
"""Adds a date to the set and updates the minimum and maximum dates""" | |
self.dates.add(date) | |
if self.minimum is None or date < self.minimum: | |
self.minimum = date | |
if self.maximum is None or date > self.maximum: | |
self.maximum = date | |
def get_next(self, date): | |
"""Returns the next date present in the set for a given date""" | |
# The implementation is a bit naive, since it loops through all the dates | |
# until finding one in the set or reaching the maximum date | |
# but it is fast enough for the purpose of this script | |
if date >= self.maximum: | |
return None | |
else: | |
while date < self.maximum: | |
date += timedelta(days=1) | |
if date in self.dates: | |
return date | |
return None | |
def get_previous(self, date): | |
"""Returns the previous date present in the set for a given date""" | |
if date <= self.minimum: | |
return None | |
else: | |
while date > self.minimum: | |
date -= timedelta(days=1) | |
if date in self.dates: | |
return date | |
return None | |
# Global variable to store all the dates found in "journal" folder | |
date_chain = DateChain() | |
def process_journals_folder(): | |
"""This method iterates over all the files in the "journals" folder | |
whose name matches the pattern YYYY_MM_DD.md | |
and for each one it calls the method "process_journal_file()" | |
passing the date and the absolute path to the file""" | |
folder = pathlib.Path("journals") | |
file_list = folder.glob("*.md") | |
to_process = [] | |
for fname in file_list: | |
name = fname.name | |
date_str = name.split(".")[0] | |
date = datetime.strptime(date_str, "%Y_%m_%d").date() | |
date_chain.add(date) | |
to_process.append((date, fname.absolute())) | |
for date, fname in to_process: | |
process_journal_file(date, fname) | |
def process_journal_file(date, fname): | |
"""This function processes a journal file and adds or updates the navigation links | |
as the first item in the file""" | |
previous_date = date_chain.get_previous(date) | |
next_date = date_chain.get_next(date) | |
previous_date = ( | |
previous_date.strftime(date_format) if previous_date else no_previous_date | |
) | |
next_date = next_date.strftime(date_format) if next_date else no_next_date | |
navigation_line = navigation_format.format( | |
previous_date=previous_date, next_date=next_date | |
) | |
with open(fname, "r", encoding="utf-8") as f: | |
lines = f.readlines() | |
# Insert the navigation line at the beginning of the file | |
for i, line in enumerate(lines): | |
# Search for the first line starting with "-" | |
if line.startswith("-"): | |
if line[:10] != navigation_line[:10]: | |
# If this file has no navigatio line, insert it | |
lines.insert(i, navigation_line) | |
else: | |
# Otherwise, update it if neccessary | |
if lines[i].rstrip() != navigation_line.rstrip(): | |
lines[i] = navigation_line | |
break | |
# Write the result in the same file | |
with open(fname, "w", encoding="utf-8") as f: | |
f.writelines(lines) | |
if __name__ == "__main__": | |
process_journals_folder() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Screenshot showing the inserted navigation line. Note that the style of the navigation line was customized via css. By default it would look like a regular item in the journal.