diff --git a/.gitignore b/.gitignore index 6373d1d..592f1dc 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,6 @@ __pycache__/ *.pyd .pytest_cache/ data/*.db +build/ +dist/ +app.spec diff --git a/README.md b/README.md index a311a90..b1668fa 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,32 @@ +# `neuron-zk-generator` + +This is a basic Python application that reads data from [my](https://github.com/thomasabishop/eolas) [zettelkasten](https://en.wikipedia.org/wiki/Zettelkasten) and +formats it so that it can be compiled as a [Neuron](https://neuron.zettel.page/) project and from there published as a static-site on the web. + ## Running app in local development -``` +```sh source venv/bin/activate +pip install -r requirements.txt neuron-zk-generator ``` + +## Build standalone executable + +Use `pyinstaller` to create single executable file. `pyinstaller` is installed +along with other packages in `requirements.txt`. + +From root: + +```sh +pyinstaller -F src/app.py +# -F compiles to single file +``` + +Outputs to `neuron-zk-generator/dist/app`. + +Sourcing the executable: + +```sh +/home/thomas/repos/neuron-zk-generator/dist/app +``` diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..5a101e3 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +termcolor==2.5.0 +pyinstaller===6.11.0 diff --git a/setup.py b/setup.py index 6ad7c9f..6f0064d 100644 --- a/setup.py +++ b/setup.py @@ -5,9 +5,7 @@ setup( version="0.1", packages=find_packages(where="src"), package_dir={"": "src"}, - install_requires=[ - # List your project dependencies here - ], + install_requires=["termcolor", "pyinstaller"], entry_points={ "console_scripts": [ "neuron-zk-generator=app:main", diff --git a/src/app.py b/src/app.py index 2437e2d..6f4dda2 100644 --- a/src/app.py +++ b/src/app.py @@ -1,12 +1,37 @@ -from constants import SOURCE -from constants import TARGET +import subprocess from lib.create_target_dir import create_target_dir from lib.transfer_files import transfer_files +from lib.transform_links import transform_links +from lib.generate_index_file import generate_index_file + +SOURCE = "/home/thomas/repos/eolas" +TARGET = "/home/thomas/repos/eolas/neuron" +SLACK_NOTIFIER = "/home/thomas/repos/slack-notifier/src/index.js" def main(): - target_dir = create_target_dir(TARGET, SOURCE) - transfer_files(f"{TARGET}/{target_dir}", SOURCE) + try: + build_id = create_target_dir(TARGET, SOURCE) + transfer_files(f"{TARGET}/{build_id}", SOURCE) + transform_links(f"{TARGET}/{build_id}") + generate_index_file(f"{TARGET}/{build_id}", build_id, SOURCE) + subprocess.run( + [ + "node", + SLACK_NOTIFIER, + "eolas", + f"✅ Neuron static site successfully generated locally for Eolas. Build: {build_id}", + ] + ) + except Exception as e: + subprocess.run( + [ + "node", + SLACK_NOTIFIER, + "eolas", + f"⛔ Neuron static site generation failed for Eolas: {e}", + ] + ) if __name__ == "__main__": diff --git a/src/constants.py b/src/constants.py deleted file mode 100644 index 42ca259..0000000 --- a/src/constants.py +++ /dev/null @@ -1,2 +0,0 @@ -SOURCE = "/home/thomas/repos/eolas" -TARGET = "/home/thomas/Desktop/output" diff --git a/src/__init__.py b/src/lib/__init__.py similarity index 100% rename from src/__init__.py rename to src/lib/__init__.py diff --git a/src/lib/constants.py b/src/lib/constants.py new file mode 100644 index 0000000..bbc8025 --- /dev/null +++ b/src/lib/constants.py @@ -0,0 +1,3 @@ +SOURCE = "/home/thomas/repos/eolas" +TARGET = "/home/thomas/repos/eolas/neuron" +SLACK_NOTIFIER = "/home/thomas/repos/slack-notifier/src/index.js" diff --git a/src/lib/create_target_dir.py b/src/lib/create_target_dir.py index a114f7c..b7bbb33 100644 --- a/src/lib/create_target_dir.py +++ b/src/lib/create_target_dir.py @@ -16,7 +16,7 @@ def create_target_dir(target_dir, source_dir): os.makedirs(f"{target_dir}/{str(unique_dir_name)}") print( colored( - f" Created new Neuron output directory: {source_dir}/{unique_dir_name}", + f"  Created new Neuron output directory: {source_dir}/{unique_dir_name}", "green", ) ) @@ -24,5 +24,7 @@ def create_target_dir(target_dir, source_dir): except Exception as e: print( - colored(f" Error occurred when creating target directory: {str(e)}", "red") + colored( + f"  Error occurred when creating target directory: {str(e)}", "red" + ) ) diff --git a/src/lib/generate_index_file.py b/src/lib/generate_index_file.py new file mode 100644 index 0000000..9a6fc9b --- /dev/null +++ b/src/lib/generate_index_file.py @@ -0,0 +1,51 @@ +from datetime import datetime +from termcolor import colored +from lib.list_entries import list_entries + + +def get_entry_titles(entries): + return [entry["file_name"] for entry in entries if entry["file_name"] != "index"] + + +def generate_wikilinks(entries): + return [f"- [[{entry}]] \n" for entry in entries] + + +def generate_index_file(target_dir, unique_dir_name, source_dir): + try: + print(colored("  Creating index file...", "blue")) + index_file = f"{target_dir}/index.md" + build_date = datetime.now() + build_date = build_date.strftime("%a %d %b %Y %H:%M:%S") + build_info = ( + f""" \n**Build ID:** {unique_dir_name}\n\n**Published:** {build_date}\n\n""" + ) + + all_notes = list_entries(f"{target_dir}") + notes_count = len(all_notes) + note_titles = sorted(get_entry_titles(all_notes)) + note_titles_formatted = generate_wikilinks(note_titles) + + recent_notes = list_entries(f"{source_dir}/zk") + recents = sorted(recent_notes, key=lambda item: item["modified"], reverse=True) + recents = recents[:8] + recents = get_entry_titles(recents) + recents_formatted = generate_wikilinks(recents) + + f = open(index_file, "a") + f.write(build_info) + f.write("### Recent edits \n\n") + + for recent in recents_formatted: + f.write(recent) + + f.write("\n\n") + f.write(f"### All notes ({notes_count}) \n\n") + + for note in note_titles_formatted: + f.write(note) + + f.close() + print(colored("  Index file created!", "green")) + except Exception as e: + print(colored(f"  Error occurred when transferring files: {str(e)}", "red")) diff --git a/src/lib/list_entries.py b/src/lib/list_entries.py new file mode 100644 index 0000000..761987b --- /dev/null +++ b/src/lib/list_entries.py @@ -0,0 +1,13 @@ +import os +from pathlib import Path + + +def list_entries(source_dir): + entries = [] + with os.scandir(source_dir) as dir_contents: + for entry in dir_contents: + if Path(entry).suffix == ".md": + file_name = Path(entry).stem + info = entry.stat() + entries.append({"file_name": file_name, "modified": info.st_mtime}) + return entries diff --git a/src/lib/transfer_files.py b/src/lib/transfer_files.py index b4008eb..8507acd 100644 --- a/src/lib/transfer_files.py +++ b/src/lib/transfer_files.py @@ -4,14 +4,27 @@ from termcolor import colored def transfer_files(target_dir, source_dir): try: - # Copy images to /static - print(colored(" Copying static files...", "blue")) - shutil.copytree(f"{source_dir}/img", f"{target_dir}/static") - print(colored(" Static files transferred!", "green")) + # Copy templates + print(colored("  Copying HTML/MD templates...", "blue")) + shutil.copytree( + f"{source_dir}/.neuron-generator/templates", target_dir, dirs_exist_ok=True + ) + neuron_template = open(f"{target_dir}/neuron.dhall", "x") + neuron_template.close() + print(colored("  Templates transferred!", "green")) + + # Copy images to /static + print(colored("  Copying static files...", "blue")) + shutil.copytree( + f"{source_dir}/img", + f"{target_dir}/static", + ) + print(colored("  Static files transferred!", "green")) + + print(colored("  Copying zettels...", "blue")) - print(colored(" Copying zettels...", "blue")) # Copy notes shutil.copytree(f"{source_dir}/zk", f"{target_dir}", dirs_exist_ok=True) - print(colored(" Zettels transferred!", "green")) + print(colored("  Zettels transferred!", "green")) except Exception as e: - print(colored(f"Error occurred when transferring files: {str(e)}", "red")) + print(colored(f"  Error occurred when transferring files: {str(e)}", "red")) diff --git a/src/lib/transform_links.py b/src/lib/transform_links.py new file mode 100644 index 0000000..a152c1d --- /dev/null +++ b/src/lib/transform_links.py @@ -0,0 +1,50 @@ +import os +import re +from termcolor import colored + +image_rgx = r"!\[.*?\]\((.*?)\)" + + +def process_image_links(line, links): + try: + for link in links: + stripped_img_ref = re.search(r"[^/\\]+$", link) + if stripped_img_ref: + stripped_img_ref = stripped_img_ref.group() + new_img_ref = f"/static/{stripped_img_ref}" + line = line.replace(f"({link})", f"({new_img_ref})") + # print(colored(f"  {links}", "green")) + return line + except Exception as e: + print(colored(f" Error when transforming link: {str(e)}", "red")) + + +def transform_links(target_dir): + print(colored("  Updating links...", "blue")) + for filename in os.listdir(target_dir): + if filename.endswith(".md"): + file_path = os.path.join(target_dir, filename) + with open(file_path, "r") as f: + lines = f.readlines() + + modified = False + new_lines = [] + for line in lines: + img_links = re.findall(image_rgx, line) + if img_links: + + new_line = process_image_links(line, img_links) + new_lines.append(new_line) + modified = True + else: + new_lines.append(line) + + if modified: + with open(file_path, "w") as f: + f.writelines(new_lines) + print( + colored( + "  Links updated!", + "green", + ) + )