From b072eed368f92025f4c7443a355eea99272dc39c Mon Sep 17 00:00:00 2001 From: dwrolvink Date: Mon, 25 Oct 2021 00:14:50 +0200 Subject: [PATCH] refactor + taglist added --- obsidianhtml/MarkdownPage.py | 22 +++++++++-- obsidianhtml/PicknickBasket.py | 11 ++++++ obsidianhtml/__init__.py | 71 ++++++++++++++++++++++++++++++---- pypi_readme.md | 7 ++-- readme.md | 6 ++- setup.cfg | 2 +- 6 files changed, 104 insertions(+), 15 deletions(-) create mode 100644 obsidianhtml/PicknickBasket.py diff --git a/obsidianhtml/MarkdownPage.py b/obsidianhtml/MarkdownPage.py index c26da55f..e1142d2f 100644 --- a/obsidianhtml/MarkdownPage.py +++ b/obsidianhtml/MarkdownPage.py @@ -33,9 +33,8 @@ def __init__(self, src_path, src_folder_path, file_tree): self.codelines = [] # Load contents of entrypoint and strip frontmatter yaml. - page = frontmatter.load(str(src_path)) - self.page = page.content - self.yaml = page + with open(src_path, encoding="utf-8") as f: + self.metadata, self.page = frontmatter.parse(f.read()) def SetDestinationPath(self, dst_folder_path, entrypoint_src_path): """Set destination path of the converted file. Both full and relative paths are set.""" @@ -64,6 +63,23 @@ def RestoreCodeSections(self): for i, value in enumerate(self.codelines): self.page = self.page.replace(f'%%%codeline-placeholder-{i}%%%', f"`{value}`") + def AddToTagtree(self, tagtree, url=''): + if 'tags' not in self.metadata: + return + + if url == '': + url = str(self.dst_path) + + for tag in self.metadata['tags']: + ctagtree = tagtree + for n, subtag in enumerate(tag.split('/')): + if subtag not in ctagtree['subtags'].keys(): + ctagtree['subtags'][subtag] = {'notes': [], 'subtags': {}} + ctagtree = ctagtree['subtags'][subtag] + + if n == (len(tag.split('/')) - 1): + ctagtree['notes'].append(url) + def ConvertObsidianPageToMarkdownPage(self, dst_folder_path, entrypoint_path, include_depth=0): """Full subroutine converting the Obsidian Code to proper markdown. Linked files are copied over to the destination folder.""" # -- Load contents diff --git a/obsidianhtml/PicknickBasket.py b/obsidianhtml/PicknickBasket.py new file mode 100644 index 00000000..93f8fde1 --- /dev/null +++ b/obsidianhtml/PicknickBasket.py @@ -0,0 +1,11 @@ +class PicknickBasket: + files = None + tagtree = None + config = None + paths = None + html_template = None + + def __init__(self, config, paths): + self.config = config + self.tagtree = {'notes': [], 'subtags': {}} + self.paths = paths \ No newline at end of file diff --git a/obsidianhtml/__init__.py b/obsidianhtml/__init__.py index 4df54f55..515f83e3 100644 --- a/obsidianhtml/__init__.py +++ b/obsidianhtml/__init__.py @@ -6,18 +6,25 @@ import markdown # convert markdown to html import yaml import urllib.parse # convert link characters like % +import frontmatter + from .MarkdownPage import MarkdownPage from .MarkdownLink import MarkdownLink from .lib import DuplicateFileNameInRoot, GetObsidianFilePath, image_suffixes +from .PicknickBasket import PicknickBasket # Open source files in the package import importlib.resources as pkg_resources -from . import src # relative-import the *package* containing the templates +from . import src # python run.py 'C:\Users\Installer\OneDrive\Obsidian\Notes' "C:\Users\Installer\OneDrive\Obsidian\Notes\Devfruits Notes.md" "output/md" "output/html" "Devfruits/Notes" -def recurseObisidianToMarkdown(page_path_str, paths, files, conf): +def recurseObisidianToMarkdown(page_path_str, pb): + paths = pb.paths + files = pb.files + conf = pb.config + # Convert path string to Path and do a double check page_path = Path(page_path_str).resolve() if page_path.exists() == False: @@ -28,6 +35,9 @@ def recurseObisidianToMarkdown(page_path_str, paths, files, conf): md = MarkdownPage(page_path, paths['obsidian_folder'], files) md.ConvertObsidianPageToMarkdownPage(paths['md_folder'], paths['obsidian_entrypoint']) + # Add yaml frontmatter back in + md.page = (frontmatter.dumps(frontmatter.Post("", **md.metadata))) + '\n' + md.page + # -- Save file # Create folder if necessary md.dst_path.parent.mkdir(parents=True, exist_ok=True) @@ -49,10 +59,14 @@ def recurseObisidianToMarkdown(page_path_str, paths, files, conf): # Convert the note that is linked to if conf['toggles']['verbose_printout']: print(f"converting {files[link_path]['fullpath']} (parent {page_path})") - recurseObisidianToMarkdown(files[link_path]['fullpath'], paths, files, conf) + recurseObisidianToMarkdown(files[link_path]['fullpath'], pb) -def ConvertMarkdownPageToHtmlPage(page_path_str, paths, files, html_template, conf): +def ConvertMarkdownPageToHtmlPage(page_path_str, pb): page_path = Path(page_path_str).resolve() + paths = pb.paths + files = pb.files + html_template = pb.html_template + conf = pb.config if page_path.exists() == False: return @@ -171,6 +185,8 @@ def ConvertMarkdownPageToHtmlPage(page_path_str, paths, files, html_template, co md.dst_path.parent.mkdir(parents=True, exist_ok=True) html_dst_path_posix = md.dst_path.as_posix()[:-3] + '.html' + md.AddToTagtree(pb.tagtree, md.dst_path.relative_to(paths['html_output_folder']).as_posix()[:-3] + '.html') + # Write html with open(html_dst_path_posix, 'w', encoding="utf-8") as f: f.write(html) @@ -195,7 +211,38 @@ def ConvertMarkdownPageToHtmlPage(page_path_str, paths, files, html_template, co if conf['toggles']['verbose_printout']: print("html: converting ", files[link_path]['fullpath'], " (parent ", md.src_path, ")") - ConvertMarkdownPageToHtmlPage(files[link_path]['fullpath'], paths, files, html_template, conf) + ConvertMarkdownPageToHtmlPage(files[link_path]['fullpath'], pb) + +def recurseTagList(tagtree, tagpath, pb): + html_url_prefix = pb.config['html_url_prefix'] + tag_dst_path = pb.paths['html_output_folder'].joinpath(f'{tagpath}index.html').resolve() + tag_dst_path_posix = tag_dst_path.as_posix() + rel_dst_path_as_posix = tag_dst_path.relative_to(pb.paths['html_output_folder']).as_posix() + + # Compile markdown + md = '' + if len(tagtree['subtags'].keys()) > 0: + md += '# Subtags\n' + for key in tagtree['subtags'].keys(): + rel_key_path_as_posix = recurseTagList(tagtree['subtags'][key], tagpath + key + '/', pb) + md += f'- [{key}](/{rel_key_path_as_posix})' + '\n' + + if len(tagtree['notes']) > 0: + md = '\n# Notes\n' + for note in tagtree['notes']: + md += f'- [{note.replace(".html", "")}]({html_url_prefix}/{note})\n' + + # Compile html + html_body = markdown.markdown(md, extensions=['extra', 'codehilite', 'toc']) + html_body = html_body.replace('', '') + html = pb.html_template.replace('{content}', html_body).replace('{title}', pb.config['site_name']).replace('{html_url_prefix}', pb.config['html_url_prefix']) + + # Write file + tag_dst_path.parent.mkdir(parents=True, exist_ok=True) + with open(tag_dst_path_posix, 'w', encoding="utf-8") as f: + f.write(html) + + return rel_dst_path_as_posix def main(): # Config @@ -276,6 +323,8 @@ def main(): paths['md_folder'].mkdir(parents=True, exist_ok=True) paths['html_output_folder'].mkdir(parents=True, exist_ok=True) + # Make "global" object that we can pass so functions + pb = PicknickBasket(conf, paths) # Convert Obsidian to markdown # ------------------------------------------ @@ -290,10 +339,12 @@ def main(): files[path.name] = {'fullpath': str(path), 'processed': False} + pb.files = files + # Start conversion with entrypoint. # Note: this will mean that any note not (indirectly) linked by the entrypoint will not be included in the output! print(f'> COMPILING MARKDOWN FROM OBSIDIAN CODE ({str(paths["obsidian_entrypoint"])})') - recurseObisidianToMarkdown(str(paths['obsidian_entrypoint']), paths, files, conf) + recurseObisidianToMarkdown(str(paths['obsidian_entrypoint']), pb) # Convert Markdown to Html @@ -321,8 +372,14 @@ def main(): rel_path_posix = path.relative_to(paths['md_folder']).as_posix() files[rel_path_posix] = {'fullpath': str(path.resolve()), 'processed': False} + pb.files = files + pb.html_template = html_template + # Start conversion from the entrypoint - ConvertMarkdownPageToHtmlPage(str(paths['md_entrypoint']), paths, files, html_template, conf) + ConvertMarkdownPageToHtmlPage(str(paths['md_entrypoint']), pb) + + # Create tag page + recurseTagList(pb.tagtree, 'tags/', pb) # Add Extra stuff to the output directories # ------------------------------------------ diff --git a/pypi_readme.md b/pypi_readme.md index 0ddc7a54..e02aad12 100644 --- a/pypi_readme.md +++ b/pypi_readme.md @@ -15,6 +15,7 @@ To convert your notes, you need to point to your notes folder, and to one note t Only notes that are found by following links recursively, starting with the entrypoint, will be converted! **Changelog**: -0.0.4: Added the option to use a custom html template, and to export the packaged template. -0.0.3: Updated readme file to work with pypi. -0.0.2: Updated readme file to work with pypi. \ No newline at end of file +- 0.0.5: Tag list added. +- 0.0.4: Added the option to use a custom html template, and to export the packaged template. +- 0.0.3: Updated readme file to work with pypi. +- 0.0.2: Updated readme file to work with pypi. \ No newline at end of file diff --git a/readme.md b/readme.md index 45ebd21e..e7e357a3 100644 --- a/readme.md +++ b/readme.md @@ -82,7 +82,7 @@ This will make sure future runs of obsidianhtml will use your custom template (p # Features ## Not supported -- Tags (you can use them in Obsidian, but they are ignored in the conversion) +- Inline tags (you can use them in Obsidian, but they are ignored in the conversion). Frontmatter tags are converted to a tag list, see below. - Possibly a lot more ## Conversion of Obsidian type links @@ -115,6 +115,10 @@ When using the format `![[Name of note]]`, the contents of the note will be incl This package also supports partial inclusions. You can use this by writing `![[Name of note#Chapter Name]]`. In this case, only that chapter and its contents until the next chapter of the same depth is included. See also [Example Website#partial-code-inclusion](https://obsidian-html.github.io/#!partial-code-inclusion). +## Frontmatter Tag list +Inline tags are excluded, but those listed in the yaml frontmatter are compiled into a list. +When running your website, go to `/tags` to view the tag list. [Example](https://obsidian-html.github.io/tags/). + ## Basic Templating All generated html code will be wrapped by the html code in `src/template.html`. This template points to `src/main.css`. Change this code *in the `/src` folder* to have the changes persist across runs of the code (output will be overwritten). diff --git a/setup.cfg b/setup.cfg index 5306104d..7cd33226 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = obsidianhtml -version = 0.0.4 +version = 0.0.5 summary = Converts Obsidian notes into proper markdown and HTML long_description = file: pypi_readme.md home-page = https://github.com/obsidian-html/obsidian-html