From fc211edc7798d7b88c3b0276edce42e402134a69 Mon Sep 17 00:00:00 2001 From: Tanishq Dubey Date: Sat, 15 Mar 2025 15:55:40 -0400 Subject: [PATCH] start docs (AI) fix some caching logic --- README.md | 185 ++++++++++++++++++++++++ config.toml.example | 6 +- docs/content/added-tools.md | 3 + docs/content/configuration.md | 19 +++ docs/content/deployment.md | 12 ++ docs/content/docker-compose-example.md | 16 ++ docs/content/example-usages.md | 39 +++++ docs/content/folder-layout.md | 0 docs/content/how-template-written.md | 23 +++ docs/content/index.md | 20 +++ docs/content/introduction.md | 5 + docs/content/jinja-primer.md | 8 + docs/content/site-setup.md | 3 + docs/content/style-setup.md | 3 + docs/content/template-setup.md | 3 + docs/content/template-style-search.md | 3 + docs/content/tool-input-return-types.md | 13 ++ docs/styles/__file.md.css | 24 +++ docs/styles/base.css | 50 +++++++ docs/templates/__file.md.html | 4 + docs/templates/base.html | 42 ++++++ src/rendering/image.py | 23 +-- src/routes/routes.py | 9 +- 23 files changed, 496 insertions(+), 17 deletions(-) create mode 100644 docs/content/added-tools.md create mode 100644 docs/content/configuration.md create mode 100644 docs/content/deployment.md create mode 100644 docs/content/docker-compose-example.md create mode 100644 docs/content/example-usages.md create mode 100644 docs/content/folder-layout.md create mode 100644 docs/content/how-template-written.md create mode 100644 docs/content/index.md create mode 100644 docs/content/introduction.md create mode 100644 docs/content/jinja-primer.md create mode 100644 docs/content/site-setup.md create mode 100644 docs/content/style-setup.md create mode 100644 docs/content/template-setup.md create mode 100644 docs/content/template-style-search.md create mode 100644 docs/content/tool-input-return-types.md create mode 100644 docs/styles/__file.md.css create mode 100644 docs/styles/base.css create mode 100644 docs/templates/__file.md.html create mode 100644 docs/templates/base.html diff --git a/README.md b/README.md index e69de29..3b65f1a 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,185 @@ +# Foldsite + +Foldsite is a dynamic site generator built with Python and Flask. It allows you to create and manage a website using Markdown content, HTML templates, and CSS styles. + +## Table of Contents + +- [Foldsite](#foldsite) + - [Table of Contents](#table-of-contents) + - [Configuration](#configuration) + - [Template Setup](#template-setup) + - [Site Setup](#site-setup) + - [Style Setup](#style-setup) + - [Template and Style Search](#template-and-style-search) + - [How a Template is Written](#how-a-template-is-written) + - [Jinja Primer](#jinja-primer) + - [Added Tools for the Template](#added-tools-for-the-template) + - [Tool Input and Return Types](#tool-input-and-return-types) + - [`get_sibling_content_files(path: str) -> list`](#get_sibling_content_filespath-str---list) + - [`get_text_document_preview(path: str) -> str`](#get_text_document_previewpath-str---str) + - [`get_sibling_content_folders(path: str) -> list`](#get_sibling_content_folderspath-str---list) + - [`get_folder_contents(path: str) -> list`](#get_folder_contentspath-str---list) + - [Example Usages for Tools and Types](#example-usages-for-tools-and-types) + - [Example Usage of `get_sibling_content_files`](#example-usage-of-get_sibling_content_files) + - [Example Usage of `get_text_document_preview`](#example-usage-of-get_text_document_preview) + - [Example Usage of `get_sibling_content_folders`](#example-usage-of-get_sibling_content_folders) + - [Example Usage of `get_folder_contents`](#example-usage-of-get_folder_contents) + - [Deployment](#deployment) + - [Docker Compose Example](#docker-compose-example) + +## Configuration + +The configuration file is written in TOML format and contains various settings for the application. Below is an example configuration file (`config.toml`): + +```toml +[paths] +content_dir = "example/content" +templates_dir = "templates" +styles_dir = "styles" + +[server] +listen_address = "127.0.0.1" +listen_port = 8080 +debug = false +access_log = true +max_threads = 4 +admin_browser = false +admin_password = "your_admin_password" +``` + +## Template Setup + +Templates are HTML files that define the structure of your web pages. They are stored in the `templates` directory. Each template can include other templates and use Jinja2 syntax for dynamic content. + +## Site Setup + +The site content is stored in the `content` directory. Each Markdown file represents a page on your site. The directory structure of the `content` directory determines the URL structure of your site. + +## Style Setup + +Styles are CSS files that define the appearance of your web pages. They are stored in the `styles` directory. You can create specific styles for different types of content and categories. + +## Template and Style Search + +Templates and styles are searched in a specific order to apply the most specific styles first, followed by more general styles, and finally the base style. + +## How a Template is Written + +Templates are written in HTML and use Jinja2 syntax for dynamic content. Below is an example template (`base.html`): + +```html + + + + + + {{ title }} + + {% for style in styles %} + + {% endfor %} + + +
+ {{ content }} +
+ + +``` + +## Jinja Primer + +Jinja2 is a templating engine for Python. It allows you to include dynamic content in your HTML templates. Below are some basic Jinja2 syntax examples: + +- Variables: `{{ variable }}` +- Loops: `{% for item in list %} ... {% endfor %}` +- Conditionals: `{% if condition %} ... {% endif %}` +- Includes: `{% include 'template.html' %}` + +## Added Tools for the Template + +Foldsite provides additional tools for templates, such as functions to get sibling content files, text document previews, and folder contents. + +## Tool Input and Return Types + +### `get_sibling_content_files(path: str) -> list` +Returns a list of sibling content files in the specified directory. + +### `get_text_document_preview(path: str) -> str` +Generates a preview of the text document located at the given path. + +### `get_sibling_content_folders(path: str) -> list` +Returns a list of sibling content folders within a specified directory. + +### `get_folder_contents(path: str) -> list` +Retrieves the contents of a folder and returns a list of `TemplateFile` objects. + +## Example Usages for Tools and Types + +### Example Usage of `get_sibling_content_files` + +```html + +``` + +### Example Usage of `get_text_document_preview` + +```html +
+ {{ get_text_document_preview('path/to/document.md') }} +
+``` + +### Example Usage of `get_sibling_content_folders` + +```html + +``` + +### Example Usage of `get_folder_contents` + +```html + +``` + +## Deployment + +To deploy Foldsite, you can use Docker. Below is an example Dockerfile: + +```dockerfile +FROM python:3.13.2-bookworm +WORKDIR /app +COPY requirements.txt requirements.txt +RUN pip install --no-cache-dir -r requirements.txt +COPY . . +CMD ["python", "main.py"] +``` + +## Docker Compose Example + +Below is an example `docker-compose.yml` file to deploy Foldsite using Docker Compose: + +```yaml +version: '3.8' +services: + foldsite: + build: . + ports: + - "8080:8080" + volumes: + - .:/app + environment: + - CONFIG_PATH=config.toml +``` \ No newline at end of file diff --git a/config.toml.example b/config.toml.example index 9bc794f..c4143cb 100644 --- a/config.toml.example +++ b/config.toml.example @@ -1,7 +1,7 @@ [paths] -content_dir = "/home/dubey/projects/foldsite/example/content" -templates_dir = "/home/dubey/projects/foldsite/example/templates" -styles_dir = "/home/dubey/projects/foldsite/example/styles" +content_dir = "/home/dubey/projects/foldsite/docs/content" +templates_dir = "/home/dubey/projects/foldsite/docs/templates" +styles_dir = "/home/dubey/projects/foldsite/docs/styles" [server] listen_address = "0.0.0.0" diff --git a/docs/content/added-tools.md b/docs/content/added-tools.md new file mode 100644 index 0000000..0be0284 --- /dev/null +++ b/docs/content/added-tools.md @@ -0,0 +1,3 @@ +# Added Tools for the Template + +Foldsite provides additional tools for templates, such as functions to get sibling content files, text document previews, and folder contents. diff --git a/docs/content/configuration.md b/docs/content/configuration.md new file mode 100644 index 0000000..83a8f86 --- /dev/null +++ b/docs/content/configuration.md @@ -0,0 +1,19 @@ +# Configuration + +The configuration file is written in TOML format and contains various settings for the application. Below is an example configuration file (`config.toml`): + +```toml +[paths] +content_dir = "example/content" +templates_dir = "templates" +styles_dir = "styles" + +[server] +listen_address = "127.0.0.1" +listen_port = 8080 +debug = false +access_log = true +max_threads = 4 +admin_browser = false +admin_password = "your_admin_password" +``` diff --git a/docs/content/deployment.md b/docs/content/deployment.md new file mode 100644 index 0000000..38268ad --- /dev/null +++ b/docs/content/deployment.md @@ -0,0 +1,12 @@ +# Deployment + +To deploy Foldsite, you can use Docker. Below is an example Dockerfile: + +```dockerfile +FROM python:3.13.2-bookworm +WORKDIR /app +COPY requirements.txt requirements.txt +RUN pip install --no-cache-dir -r requirements.txt +COPY . . +CMD ["python", "main.py"] +``` diff --git a/docs/content/docker-compose-example.md b/docs/content/docker-compose-example.md new file mode 100644 index 0000000..72e4e49 --- /dev/null +++ b/docs/content/docker-compose-example.md @@ -0,0 +1,16 @@ +# Docker Compose Example + +Below is an example `docker-compose.yml` file to deploy Foldsite using Docker Compose: + +```yaml +version: '3.8' +services: + foldsite: + build: . + ports: + - "8080:8080" + volumes: + - .:/app + environment: + - CONFIG_PATH=config.toml +``` diff --git a/docs/content/example-usages.md b/docs/content/example-usages.md new file mode 100644 index 0000000..937657a --- /dev/null +++ b/docs/content/example-usages.md @@ -0,0 +1,39 @@ +# Example Usages for Tools and Types + +### Example Usage of `get_sibling_content_files` + +```html + +``` + +### Example Usage of `get_text_document_preview` + +```html +
+ {{ get_text_document_preview('path/to/document.md') }} +
+``` + +### Example Usage of `get_sibling_content_folders` + +```html + +``` + +### Example Usage of `get_folder_contents` + +```html + +``` diff --git a/docs/content/folder-layout.md b/docs/content/folder-layout.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/content/how-template-written.md b/docs/content/how-template-written.md new file mode 100644 index 0000000..47b28bc --- /dev/null +++ b/docs/content/how-template-written.md @@ -0,0 +1,23 @@ +# How a Template is Written + +Templates are written in HTML and use Jinja2 syntax for dynamic content. Below is an example template (`base.html`): + +```html + + + + + + {{ title }} + + {% for style in styles %} + + {% endfor %} + + +
+ {{ content }} +
+ + +``` diff --git a/docs/content/index.md b/docs/content/index.md new file mode 100644 index 0000000..086712f --- /dev/null +++ b/docs/content/index.md @@ -0,0 +1,20 @@ +# Foldsite Documentation + +Welcome to the Foldsite documentation. This site will guide you through the setup, configuration, and usage of Foldsite. + +## Table of Contents + +1. [Introduction](introduction.md) +2. [Configuration](configuration.md) +3. [Template Setup](template-setup.md) +4. [Site Setup](site-setup.md) +5. [Style Setup](style-setup.md) +6. [Template and Style Search](template-style-search.md) +7. [How a Template is Written](how-template-written.md) +8. [Jinja Primer](jinja-primer.md) +9. [Added Tools for the Template](added-tools.md) +10. [Tool Input and Return Types](tool-input-return-types.md) +11. [Example Usages for Tools and Types](example-usages.md) +12. [Deployment](deployment.md) +13. [Docker Compose Example](docker-compose-example.md) +14. [Folder Layout](folder-layout.md) diff --git a/docs/content/introduction.md b/docs/content/introduction.md new file mode 100644 index 0000000..9721717 --- /dev/null +++ b/docs/content/introduction.md @@ -0,0 +1,5 @@ +# Introduction + +Foldsite is a dynamic site generator built with Python and Flask. It allows you to create and manage a website using Markdown content, HTML templates, and CSS styles. + +This documentation will guide you through the setup, configuration, and usage of Foldsite. diff --git a/docs/content/jinja-primer.md b/docs/content/jinja-primer.md new file mode 100644 index 0000000..4309c66 --- /dev/null +++ b/docs/content/jinja-primer.md @@ -0,0 +1,8 @@ +# Jinja Primer + +Jinja2 is a templating engine for Python. It allows you to include dynamic content in your HTML templates. Below are some basic Jinja2 syntax examples: + +- Variables: `{{ variable }}` +- Loops: `{% for item in list %} ... {% endfor %}` +- Conditionals: `{% if condition %} ... {% endif %}` +- Includes: `{% include 'template.html' %}` diff --git a/docs/content/site-setup.md b/docs/content/site-setup.md new file mode 100644 index 0000000..856ca18 --- /dev/null +++ b/docs/content/site-setup.md @@ -0,0 +1,3 @@ +# Site Setup + +The site content is stored in the `content` directory. Each Markdown file represents a page on your site. The directory structure of the `content` directory determines the URL structure of your site. diff --git a/docs/content/style-setup.md b/docs/content/style-setup.md new file mode 100644 index 0000000..a33fbbf --- /dev/null +++ b/docs/content/style-setup.md @@ -0,0 +1,3 @@ +# Style Setup + +Styles are CSS files that define the appearance of your web pages. They are stored in the `styles` directory. You can create specific styles for different types of content and categories. diff --git a/docs/content/template-setup.md b/docs/content/template-setup.md new file mode 100644 index 0000000..ebc33fa --- /dev/null +++ b/docs/content/template-setup.md @@ -0,0 +1,3 @@ +# Template Setup + +Templates are HTML files that define the structure of your web pages. They are stored in the `templates` directory. Each template can include other templates and use Jinja2 syntax for dynamic content. diff --git a/docs/content/template-style-search.md b/docs/content/template-style-search.md new file mode 100644 index 0000000..6fe8a43 --- /dev/null +++ b/docs/content/template-style-search.md @@ -0,0 +1,3 @@ +# Template and Style Search + +Templates and styles are searched in a specific order to apply the most specific styles first, followed by more general styles, and finally the base style. diff --git a/docs/content/tool-input-return-types.md b/docs/content/tool-input-return-types.md new file mode 100644 index 0000000..f2896d8 --- /dev/null +++ b/docs/content/tool-input-return-types.md @@ -0,0 +1,13 @@ +# Tool Input and Return Types + +### `get_sibling_content_files(path: str) -> list` +Returns a list of sibling content files in the specified directory. + +### `get_text_document_preview(path: str) -> str` +Generates a preview of the text document located at the given path. + +### `get_sibling_content_folders(path: str) -> list` +Returns a list of sibling content folders within a specified directory. + +### `get_folder_contents(path: str) -> list` +Retrieves the contents of a folder and returns a list of `TemplateFile` objects. diff --git a/docs/styles/__file.md.css b/docs/styles/__file.md.css new file mode 100644 index 0000000..e471e26 --- /dev/null +++ b/docs/styles/__file.md.css @@ -0,0 +1,24 @@ +article { + max-width: 800px; + margin: 0 auto; +} + +article h1, article h2, article h3, article h4, article h5, article h6 { + margin-top: 1.5rem; +} + +article p { + margin: 1rem 0; +} + +article pre { + background: #f4f4f4; + padding: 1rem; + overflow-x: auto; +} + +article code { + background: #f4f4f4; + padding: 0.2rem 0.4rem; + border-radius: 3px; +} diff --git a/docs/styles/base.css b/docs/styles/base.css new file mode 100644 index 0000000..2361ccf --- /dev/null +++ b/docs/styles/base.css @@ -0,0 +1,50 @@ +body { + font-family: Arial, sans-serif; + line-height: 1.6; + margin: 0; + padding: 0; +} + +header { + background: #333; + color: #fff; + padding: 1rem 0; + text-align: center; +} + +header h1 { + margin: 0; +} + +nav ul { + list-style: none; + padding: 0; +} + +nav ul li { + display: inline; + margin-right: 1rem; +} + +nav ul li a { + color: #fff; + text-decoration: none; +} + +nav ul li a:hover { + text-decoration: underline; +} + +main { + padding: 2rem; +} + +footer { + background: #333; + color: #fff; + text-align: center; + padding: 1rem 0; + position: fixed; + bottom: 0; + width: 100%; +} diff --git a/docs/templates/__file.md.html b/docs/templates/__file.md.html new file mode 100644 index 0000000..bad5d05 --- /dev/null +++ b/docs/templates/__file.md.html @@ -0,0 +1,4 @@ +
+ {{ content|safe }} +
+ diff --git a/docs/templates/base.html b/docs/templates/base.html new file mode 100644 index 0000000..024b756 --- /dev/null +++ b/docs/templates/base.html @@ -0,0 +1,42 @@ + + + + + + {{ title }} + + {% for style in styles %} + + {% endfor %} + + +
+

Foldsite Documentation

+ +
+
+ {{ content|safe }} +
+ + + diff --git a/src/rendering/image.py b/src/rendering/image.py index 556206e..506c320 100644 --- a/src/rendering/image.py +++ b/src/rendering/image.py @@ -1,12 +1,9 @@ from PIL import Image from io import BytesIO -from functools import cache - -@cache -def generate_thumbnail(image_path, resize_percent, min_width): - # Generate a unique key based on the image path, resize percentage, and minimum width - key = f"{image_path}_{resize_percent}_{min_width}" +from functools import lru_cache +@lru_cache(maxsize=512) +def generate_thumbnail(image_path, resize_percent, min_width, max_width): # Open the image file with Image.open(image_path) as img: # Calculate the new size based on the resize percentage @@ -20,13 +17,19 @@ def generate_thumbnail(image_path, resize_percent, min_width): new_width = min_width new_height = int(new_height * scale_factor) + # Ensure the maximum width is not exceeded + if new_width > max_width: + scale_factor = max_width / new_width + new_width = max_width + new_height = int(new_height * scale_factor) + # Resize the image while maintaining the aspect ratio img.thumbnail((new_width, new_height)) # Rotate the image based on the EXIF orientation tag try: - exif = img._getexif() - orientation = exif.get(0x0112, 1) # 0x0112 is the EXIF orientation tag + exif = img.info['exif'] + orientation = img._getexif().get(0x0112, 1) # 0x0112 is the EXIF orientation tag if orientation == 3: img = img.rotate(180, expand=True) elif orientation == 6: @@ -35,12 +38,12 @@ def generate_thumbnail(image_path, resize_percent, min_width): img = img.rotate(90, expand=True) except (AttributeError, KeyError, IndexError): # cases: image don't have getexif - pass + exif = None # Save the thumbnail to a BytesIO object thumbnail_io = BytesIO() img_format = img.format if img.format in ["JPEG", "JPG", "PNG"] else "JPEG" - img.save(thumbnail_io, format=img_format) + img.save(thumbnail_io, format=img_format, exif=exif) thumbnail_io.seek(0) return (thumbnail_io.getvalue(), img_format) \ No newline at end of file diff --git a/src/routes/routes.py b/src/routes/routes.py index 4d507ea..24254e6 100644 --- a/src/routes/routes.py +++ b/src/routes/routes.py @@ -1,9 +1,8 @@ from pathlib import Path from src.config.config import Configuration from src.rendering.renderer import render_page, render_error_page -from flask import send_file +from flask import send_file, request from src.rendering.image import generate_thumbnail -from functools import lru_cache import os @@ -114,13 +113,15 @@ class RouteManager: if file_path.exists(): # Check to see if the file is an image, if it is, render a thumbnail if file_path.suffix.lower() in [".jpg", ".jpeg", ".png", ".gif"]: + max_width = request.args.get("max_width", default=2048, type=int) thumbnail_bytes, img_format = generate_thumbnail( - str(file_path), 10, 2048 + str(file_path), 10, 2048, max_width ) return ( thumbnail_bytes, 200, - {"Content-Type": f"image/{img_format.lower()}"}, + {"Content-Type": f"image/{img_format.lower()}", + "cache-control": "public, max-age=31536000"}, ) return send_file(file_path) else: