diff --git a/.gitignore b/.gitignore index 9168178..ce63e47 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -events -mo \ No newline at end of file +venv +dist \ No newline at end of file diff --git a/README.md b/README.md index cbb424f..445b5d3 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,72 @@ -# Klank docs +# README.md -## installation -Run the install.sh to install [Mustache](https://github.com/tests-always-included/mo) +### Installation +Create a virtual python environment using ` python -m venv venv` +Activate the virtual environment using `source venv/bin/activate` +Install the packages using `pip install -r requirements.txt` -Also, pandoc is needed. Additionally researching this thing https://github.com/pandoc-ext/include-files/tree/main +### Running +Compile the content into a static site using +``` +python app.py +``` + +This creates the "dist" directory, with all of the HTML in there. +At the moment, `dist` is part of the `.gitignore`! -More info to come ;) +### Writing Content +Within the `content` directory, you can create/edit markdown files. When compiling the markdown content, `app.py` will look for jinja templates in the `templates` directory. If the markdown file is in the root of `content`, it will try to load a template with the same filename as the markdown file. For instance, `about.md` will look for `about.jinja`. +If the markdown file is in a subdirectory, it will look for a template with the same name as the subdirectory. At the moment, there is no functionality for deep nested folders. So, `recipes/tomato-soup.md` wants `recipes.jinja`. If the template does not exist, the default template is `post.jinja`. -## Scripts & folders +The project uses [Jinja](https://jinja.palletsprojects.com/), which allows for extending templates, using variables, looping trough variables, and other funky features! -- `newsletters` write your new newsletter here -- `templates` contains a markdown template for events, that are being grabbed via -- `get_events.sh` run as `sh get_events.sh` to grab all events from the klankschool calendar and store them as markdwon files -- `install.sh` the installation script for Mustache -- `make_newsletter.sh` run as `sh make_newsletter` to translate all created newsletters in the `newsletters` directory into HTML. Created newsletters are stored in the `dist` directory +Additionally, `component-inventory.csv` is loaded as a dataset. + +### Transclusion +You'll be able to transclude one markdown file into another, by applying the following syntax: + +```MARKDOWN +{! path-to-file.md !} +``` + +The transclusion package also allows for transcluding specific lines: + +```MARKDOWN +{! path-to-file.md!lines=1 3 8-10 2} +``` +In the example above, it would read the file and include the lines 1, 3, 8, 9, 10, 2. + + + + +### Metadata +Metadata can be applied to markdown files using FrontMatters YAML. The metadata is accessable in the jinja tempaltes using `page.{metadata}`. In the example below, this could be, `page.title`. + +```MARKDOWN +--- +excerpt: tl;dr +title: Capacitors +type: Capacitor +valueSymbol: unf +description: This is the description +--- +``` + + +### Assets +At the moment, the `src/assets` directory is copied to the `dist` folder, to apply CSS. + + +### TODO +- [ ] update readme +- [ ] fix issue with YAML data showing up in included files +- [ ] lurk newsletter? +- [ ] minify/gzip the HTML +- [ ] Export to PDF +- [ ] Export to mediawiki +- [ ] Export to PDF with zine layout +- [ ] last edited date +- [ ] Direct link to the markdown file in git? +- [ ] Markdown page breaks \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 0000000..aa6f3ad --- /dev/null +++ b/app.py @@ -0,0 +1,115 @@ +import os +from pathlib import Path +import shutil +import csv + +from jinja2 import Environment, PackageLoader, select_autoescape +import frontmatter +import markdown +from markdown_include.include import MarkdownInclude +from slugify import slugify + +markdown_include = MarkdownInclude( + configs={'base_path': 'src/content/'} +) + +env = Environment( + loader=PackageLoader("src"), + autoescape=select_autoescape() +) + +CONTENT_D = os.path.abspath("src/content") +OUTPUT_D = "dist" +documents = {} + +def slugify_filter(value): + return slugify(value) + +env.filters["slugify"] = slugify_filter + + +def render_single_file(template, page, path, dist): + html = template.render(documents=documents, page=page) + name = Path(path).stem + + if not os.path.exists(dist): + os.makedirs(dist) + + with open(f"{dist}/{name}.html", "w", encoding="utf-8") as output_file: + output_file.write(html) + + +def get_page_data(path): + filename = Path(path).stem + page = frontmatter.load(path) + page['slug'] = slugify(filename) + page.body = markdown.markdown(page.content, extensions=['def_list', 'footnotes', markdown_include]) + + return page + + +def render_posts(path): + name = Path(path).stem + print(f"looking for {name}.jinja") + template = env.select_template([f"{name}.jinja", "post.jinja"]) + + for filename in os.listdir(path): + if filename.endswith(".md"): + post_path = os.path.join(path, filename) + page = get_page_data(post_path) + render_single_file(template, page, post_path, f"{OUTPUT_D}/{name}") + + +def preload_documents(): + print("preload any needed data") + + for subdir in os.listdir(CONTENT_D): + path = os.path.join(CONTENT_D, subdir) + + if os.path.isdir(path): + name = Path(path).stem + if name not in documents: + documents[name] = [] + for filename in os.listdir(path): + if filename.endswith(".md"): + post_path = os.path.join(path, filename) + + documents[name].append(get_page_data(post_path)) + + elif Path(path).suffix == '.md': + documents[Path(path).stem] = get_page_data(path) + +def copy_assets(): + if os.path.exists("dist/assets"): + shutil.rmtree("dist/assets") + + shutil.copytree("src/assets", "dist/assets") + +def get_inventory(): + + with open("src/content/component-inventory.csv") as f: + documents['inventory'] = [] + for line in csv.DictReader(f, fieldnames=('ID', 'Name', 'Value', 'Type', 'Date', 'Where')): + documents['inventory'].append(line) + +def main(): + get_inventory() + preload_documents() + + print("render the content") + for subdir in os.listdir(CONTENT_D): + path = os.path.join(CONTENT_D, subdir) + + if os.path.isdir(path): + render_posts(path) + elif Path(path).suffix == '.md': + template = env.select_template( + [f"{Path(path).stem}.jinja", "post.jinja"]) + page = get_page_data(path) + render_single_file(template, page, path, OUTPUT_D) + else: + print("i cannot handle this file yet") + + copy_assets() + +main() diff --git a/dist/newsletters/december.md.html b/dist/newsletters/december.md.html deleted file mode 100644 index 404c568..0000000 --- a/dist/newsletters/december.md.html +++ /dev/null @@ -1,6 +0,0 @@ -

December

-

What a great month it was

-

Event

-

Let’s (un)repair things -together!

-

This event starts at 1731603600

diff --git a/get_events.sh b/get_events.sh index dc8ea16..4f4f643 100644 --- a/get_events.sh +++ b/get_events.sh @@ -22,8 +22,9 @@ echo $(curl -s "https://calendar.klank.school/api/events") | jq -c '.[]' | while rm -f -- $filename fi - mo templates/event.md >> $filename - echo "Created a new event markdown file for " $filename + # mo templates/event.md >> $filename + # echo "Created a new event markdown file for " $filename + echo "Whoops! This template is not accessible anymore. Please update me :) " done diff --git a/include-files.lua b/include-files.lua deleted file mode 100644 index 69df689..0000000 --- a/include-files.lua +++ /dev/null @@ -1,127 +0,0 @@ ---- include-files.lua – filter to include Markdown files ---- ---- Copyright: © 2019–2021 Albert Krewinkel ---- License: MIT – see LICENSE file for details - --- Module pandoc.path is required and was added in version 2.12 -PANDOC_VERSION:must_be_at_least '2.12' - -local List = require 'pandoc.List' -local path = require 'pandoc.path' -local system = require 'pandoc.system' - ---- Get include auto mode -local include_auto = false -function get_vars (meta) - if meta['include-auto'] then - include_auto = true - end -end - ---- Keep last heading level found -local last_heading_level = 0 -function update_last_level(header) - last_heading_level = header.level -end - ---- Update contents of included file -local function update_contents(blocks, shift_by, include_path) - local update_contents_filter = { - -- Shift headings in block list by given number - Header = function (header) - if shift_by then - header.level = header.level + shift_by - end - return header - end, - -- If link paths are relative then prepend include file path - Link = function (link) - if path.is_relative(link.target) then - link.target = path.normalize(path.join({include_path, link.target})) - end - return link - end, - -- If image paths are relative then prepend include file path - Image = function (image) - if path.is_relative(image.src) then - image.src = path.normalize(path.join({include_path, image.src})) - end - return image - end, - -- Update path for include-code-files.lua filter style CodeBlocks - CodeBlock = function (cb) - if cb.attributes.include and path.is_relative(cb.attributes.include) then - cb.attributes.include = - path.normalize(path.join({include_path, cb.attributes.include})) - end - return cb - end - } - - return pandoc.walk_block(pandoc.Div(blocks), update_contents_filter).content -end - ---- Filter function for code blocks -local transclude -function transclude (cb) - -- ignore code blocks which are not of class "include". - if not cb.classes:includes 'include' then - return - end - - -- Markdown is used if this is nil. - local format = cb.attributes['format'] - - -- Attributes shift headings - local shift_heading_level_by = 0 - local shift_input = cb.attributes['shift-heading-level-by'] - if shift_input then - shift_heading_level_by = tonumber(shift_input) - else - if include_auto then - -- Auto shift headings - shift_heading_level_by = last_heading_level - end - end - - --- keep track of level before recusion - local buffer_last_heading_level = last_heading_level - - local blocks = List:new() - for line in cb.text:gmatch('[^\n]+') do - if line:sub(1,2) ~= '//' then - local fh = io.open(line) - if not fh then - io.stderr:write("Cannot open file " .. line .. " | Skipping includes\n") - else - -- read file as the given format with global reader options - local contents = pandoc.read( - fh:read '*a', - format, - PANDOC_READER_OPTIONS - ).blocks - last_heading_level = 0 - -- recursive transclusion - contents = system.with_working_directory( - path.directory(line), - function () - return pandoc.walk_block( - pandoc.Div(contents), - { Header = update_last_level, CodeBlock = transclude } - ) - end).content - --- reset to level before recursion - last_heading_level = buffer_last_heading_level - blocks:extend(update_contents(contents, shift_heading_level_by, - path.directory(line))) - fh:close() - end - end - end - return blocks -end - -return { - { Meta = get_vars }, - { Header = update_last_level, CodeBlock = transclude } -} diff --git a/install.sh b/install.sh deleted file mode 100644 index 9c10a57..0000000 --- a/install.sh +++ /dev/null @@ -1,11 +0,0 @@ -# Download -curl -sSL https://raw.githubusercontent.com/tests-always-included/mo/master/mo -o mo - -# Make executable -chmod +x mo - -# Move to the right folder -sudo mv mo /usr/local/bin/ - -# Test -echo "works" | mo \ No newline at end of file diff --git a/make_newsletter.sh b/make_newsletter.sh deleted file mode 100644 index c6218c5..0000000 --- a/make_newsletter.sh +++ /dev/null @@ -1,10 +0,0 @@ - -mkdir dist -mkdir dist/newsletters - -cd newsletters - -for file in *.md; do - echo $file - pandoc --lua-filter=../include-files.lua $file --output ../dist/newsletters/$file.html -done \ No newline at end of file diff --git a/newsletters/december.md b/newsletters/december.md deleted file mode 100644 index bf30cf2..0000000 --- a/newsletters/december.md +++ /dev/null @@ -1,9 +0,0 @@ -# December - -What a great month it was - -## Event - -``` {.include} -../events/lets-unrepair-things-together-2.md -``` \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..ced814a --- /dev/null +++ b/requirements.txt @@ -0,0 +1,10 @@ +importlib_metadata==8.5.0 +Jinja2==3.1.4 +Markdown==3.7 +markdown-include==0.8.1 +MarkupSafe==3.0.2 +python-frontmatter==1.1.0 +python-slugify==8.0.4 +PyYAML==5.1 +text-unidecode==1.3 +zipp==3.21.0 diff --git a/src/assets/image.png b/src/assets/image.png new file mode 100644 index 0000000..8743c04 Binary files /dev/null and b/src/assets/image.png differ diff --git a/src/assets/style.css b/src/assets/style.css new file mode 100644 index 0000000..75b4025 --- /dev/null +++ b/src/assets/style.css @@ -0,0 +1,92 @@ +* { + box-sizing: border-box; +} + +body, html { + margin: 0 0; + padding: 0 0; + width: 100%; + height: 100%; +} + +body { + font-family: sans-serif; + background-color: floralwhite; +} + + +a, a[visited] { + color: orange; +} + +ul { + padding-left: 2ch; +} + +main { + display: grid; + grid-template-columns: 3fr 1fr; + grid-template-rows: 1fr min-content; + max-width: calc(1200px - 2rem); + margin: 0rem auto; + grid-gap: 1rem; +} + +aside { + position: sticky; + top: 0; + padding: 1rem 1rem 1rem 0; +} + +@media only screen and (max-width: 600px) or print{ + main { + grid-template-columns: 1fr; + margin: unset; + } +} + +@media (prefers-color-scheme: dark) { + body { + background-color: #1b1b1b; + color: white; + } +} + +aside h2 { + font-size: 1.25rem; +} + +article { + line-height: 150%; + max-width: 80ch; + margin: 0 auto; + display: flex; + flex-direction: column; +} + +article img { + max-width: 90ch; + margin-left: -5ch; +} + +.footnote { + order: 99999999999; +} + +.footnote ol { + line-height: 100%; + font-size: 80%; +} + +table { + width: 100%; + border-spacing: 0px; + table-layout: fixed; + text-align: left; +} + +th, td { + border-bottom: 1px solid rgba(255,255,255,1); + padding: 8px 8px; +} + diff --git a/src/content/component-inventory.csv b/src/content/component-inventory.csv new file mode 100644 index 0000000..1d90fb8 --- /dev/null +++ b/src/content/component-inventory.csv @@ -0,0 +1,4 @@ +ID,Name,Value,Type,Date,Where +1,Resistor,220,Resistor,25/11/2024,Reel to Reel recorder +2,Resistor,440,Resistor,25/11/2024,Reel to Reel recorder +2,Resistor,440,Resistor,25/11/2024,Reel to Reel recorder \ No newline at end of file diff --git a/src/content/components/capacitors.md b/src/content/components/capacitors.md new file mode 100644 index 0000000..53e47a8 --- /dev/null +++ b/src/content/components/capacitors.md @@ -0,0 +1,9 @@ +--- +excerpt: tl;dr +title: Capacitors +type: Capacitor +valueSymbol: unf +description: This is the description +--- + +# A text about capacitors \ No newline at end of file diff --git a/src/content/components/resistors.md b/src/content/components/resistors.md new file mode 100644 index 0000000..73cfa1e --- /dev/null +++ b/src/content/components/resistors.md @@ -0,0 +1,9 @@ +--- +excerpt: tl;dr +title: Resistors +type: Resistor +valueSymbol: Ω +description: This is the description +--- + +# A text about resistors \ No newline at end of file diff --git a/src/content/index.md b/src/content/index.md new file mode 100644 index 0000000..06f7353 --- /dev/null +++ b/src/content/index.md @@ -0,0 +1,9 @@ +--- +excerpt: A documentation page +title: Homepage +description: This is the description of the about us page +--- + +# My documentation + +Well, hello there, world. \ No newline at end of file diff --git a/src/content/newsletters/december.md b/src/content/newsletters/december.md new file mode 100644 index 0000000..aba278e --- /dev/null +++ b/src/content/newsletters/december.md @@ -0,0 +1,10 @@ +--- +title: A newsletter for december +--- + +# Example of a newsletter +Hello world, this is a newsletter being generated for the month December. Keen to check out what has been happening? Here are some repair logs i wanted to share with you. + +### Repair logs in december + +{! repair-logs/21112024.md !} diff --git a/src/content/recipes/two.md b/src/content/recipes/two.md new file mode 100644 index 0000000..863675a --- /dev/null +++ b/src/content/recipes/two.md @@ -0,0 +1,7 @@ +--- +excerpt: tl;dr +title: Hello, world! +description: This is the description +--- + +This is my recipe! \ No newline at end of file diff --git a/src/content/repair-logs/21112024.md b/src/content/repair-logs/21112024.md new file mode 100644 index 0000000..9edfbd9 --- /dev/null +++ b/src/content/repair-logs/21112024.md @@ -0,0 +1,9 @@ +--- +excerpt: tl;dr +layout: post +title: Hello, world! +description: This is the description +--- + +# Repair log 21st november 2024 +We had a great time today. \ No newline at end of file diff --git a/src/templates/base.jinja b/src/templates/base.jinja new file mode 100644 index 0000000..f3d8dab --- /dev/null +++ b/src/templates/base.jinja @@ -0,0 +1,52 @@ + + + + + + Document + + + + + +
+
+ {% block content %}{% endblock %} +
+ + +
+ + + + \ No newline at end of file diff --git a/src/templates/components.jinja b/src/templates/components.jinja new file mode 100644 index 0000000..90e14be --- /dev/null +++ b/src/templates/components.jinja @@ -0,0 +1,49 @@ +{% extends "base.jinja" %} + +{% block content %} +

template: components.jinja

+ + +
+

{{page['title']}}

+ {{ page['body'] }} + +

Component inventory

+ +{% set ns = namespace(has_inventory=false) %} +{% for item in documents.inventory %} + {% if item.Type.capitalize() == page['type'].capitalize() %} + {% set ns.has_inventory = true %} + hai + {% endif %} +{% endfor %} + +{% if ns.has_inventory %} + + + + + + + + + + {% for item in documents.inventory %} + {% if item.Type.capitalize() == page.type.capitalize() %} + + + + + + {% endif %} + {% endfor %} + + +
NameWhereDate
{{item.Value}}{{page.valueSymbol}}{{item.Where}}{{item.Date}}
+ +{% else %} +No components were identified yet +{% endif %} + +
+{% endblock %} \ No newline at end of file diff --git a/src/templates/index.jinja b/src/templates/index.jinja new file mode 100644 index 0000000..2bea713 --- /dev/null +++ b/src/templates/index.jinja @@ -0,0 +1,6 @@ +{% extends "base.jinja" %} + +{% block content %} +

This is the index

+ +{% endblock %} \ No newline at end of file diff --git a/src/templates/post.jinja b/src/templates/post.jinja new file mode 100644 index 0000000..e2e2009 --- /dev/null +++ b/src/templates/post.jinja @@ -0,0 +1,11 @@ +{% extends "base.jinja" %} + +{% block content %} +

template: post.jinja

+ + +
+

{{page['title']}}

+ {{ page['body'] }} +
+{% endblock %} \ No newline at end of file diff --git a/templates/event.md b/templates/event.md deleted file mode 100644 index caac345..0000000 --- a/templates/event.md +++ /dev/null @@ -1,2 +0,0 @@ -# {{TITLE}} -This event starts at {{START_DATETIME}} \ No newline at end of file