presskit


Namepresskit JSON
Version 0.0.5 PyPI version JSON
download
home_pageNone
SummaryStatic site generator
upload_time2025-07-14 12:18:42
maintainerNone
docs_urlNone
authorNone
requires_python>=3.11
licenseNone
keywords static site generator static sites ssg
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Presskit

A powerful static site generator that combines Markdown content with Jinja2 templating and database-driven page generation. Presskit lets you build dynamic static sites by connecting your content to SQLite databases and JSON data sources.

## Key Features

- **Jinja2 Templating**: Use Jinja2 variables and logic in both Markdown content and HTML layouts
- **Multiple Data Sources**: Connect to SQLite, PostgreSQL, DuckDB databases, and JSON files with JSONPath querying
- **Dynamic Page Generation**: Generate multiple pages automatically from database query results
- **Structured Context**: Access site metadata, build information, and data through a clean template context

## Installation

```bash
pip install presskit
```

Or you can use [Astral's uv](https://docs.astral.sh/uv/) Python package manager to install Presskit as a self-contained tool so it can be run from the command line without needing to activate a virtual environment:

```bash
uv tool install presskit
```

### Database Dependencies

Presskit supports different data sources. Install additional dependencies based on your needs:

```bash
# For PostgreSQL support
pip install presskit[postgresql]

# For DuckDB support  
pip install presskit[duckdb]
```

## Quick Start

1. Create a new site directory:
```bash
mkdir my-site
cd my-site
```

2. Create the basic structure:
```
my-site/
├── presskit.json      # Configuration file
├── content/           # Markdown files
├── templates/         # HTML templates
└── public/            # Generated output (created automatically)
```

3. Build your site:
```bash
presskit build
```

## Basic Usage

### Writing Markdown Content

Create Markdown files in the `content/` directory. Each file can include YAML front matter for metadata:

```
---
title: "Welcome to My Site"
description: "A brief introduction"
layout: page
---

# Welcome

This is my **awesome** site built with Presskit!
```

### Creating HTML Templates

Templates go in the `templates/` directory. Here's a basic `page.html` template:

```html
<!DOCTYPE html>
<html lang="{{ site.language }}">
<head>
    <meta charset="UTF-8">
    <title>{{ page.title or site.title }}</title>
    <meta name="description" content="{{ page.description or site.description }}">
</head>
<body>
    <header>
        <h1>{{ site.title }}</h1>
    </header>
    
    <main>
        {{ page.content }}
    </main>
    
    <footer>
        <p>&copy; {{ build.year }} {{ site.author }}</p>
    </footer>
</body>
</html>
```

### Configuration

Create a `presskit.json` file to configure your site:

```json
{
    "title": "My Awesome Site",
    "description": "A site built with Presskit",
    "author": "Your Name",
    "url": "https://mysite.com",
    "language": "en-US"
}
```

## Template Variables

Presskit provides a structured context with the following variables available in all templates:

### Site Variables (`site.*`)
- `site.title` - Site title
- `site.description` - Site description  
- `site.author` - Site author
- `site.url` - Base site URL
- `site.version` - Site version
- `site.language` - Site language

### Build Variables (`build.*`)
- `build.date` - Build date (YYYY-MM-DD)
- `build.year` - Build year
- `build.timestamp` - Full build timestamp
- `build.iso_date` - Build date in ISO format

### Page Variables (`page.*`)
- `page.filename` - Page filename without extension
- `page.filepath` - Full file path
- `page.path` - Clean URL path
- `page.layout` - Template layout name
- `page.content` - Processed HTML content (in templates)
- `page.title` - Page title from front matter
- `page.description` - Page description from front matter

### Data Variables (`data.*`)
- `data.queries` - Results from named queries
- `data.sources` - JSON data sources
- `data.page_queries` - Page-specific query results

Plus any custom variables from your front matter are available at the top level.

## Using Variables in Markdown

You can use Jinja2 templating directly in your Markdown content:

```
---
title: About
category: personal
---

# About {{ site.author }}

This site was built on {{ build.date }} and is currently version {{ site.version }}.

{% if category == "personal" %}
This is a personal page about {{ site.author }}.
{% endif %}
```

## Data Sources and Queries

Presskit's data integration feature allows you to connect your static site to data sources, enabling content generation while maintaining the performance benefits of static sites. This powerful feature bridges the gap between static and dynamic websites.

This enables data-driven pages that display statistics, reports, or any structured data. Ideal for portfolios showcasing project metrics, business dashboards, or documentation sites pulling from APIs.

This encourages separation of concerns where you keep your content in databases where it can be easily edited, queried, and managed, while your site structure remains in version control.

### Configuring Data Sources

Presskit supports multiple data source types. Add them to your `presskit.json`:

#### SQLite

```json
{
    "sources": {
        "blog_db": {
            "type": "sqlite",
            "path": "data/blog.db"
        }
    }
}
```

#### PostgreSQL

```json
{
    "sources": {
        "postgres_db": {
            "type": "postgresql", 
            "host": "localhost",
            "port": 5432,
            "database": "mydb",
            "username": "user",
            "password": "env:DB_PASSWORD"
        }
    }
}
```

#### DuckDB

```json
{
    "sources": {
        "analytics_db": {
            "type": "duckdb",
            "path": "data/analytics.duckdb"
        }
    }
}
```

#### JSON Files

```json
{
    "sources": {
        "config": {
            "type": "json",
            "path": "data/site-config.json"
        }
    }
}
```

JSON sources support both basic data loading and advanced JSONPath querying for extracting specific data from complex JSON structures.

#### Connection Strings

You can also use connection strings for database sources:

```json
{
    "sources": {
        "prod_db": {
            "type": "postgresql",
            "connection_string": "env:DATABASE_URL"
        }
    }
}
```

### JSON Data Querying

JSON sources support powerful JSONPath expressions for extracting data from complex JSON structures. JSONPath is a query language for JSON, similar to XPath for XML.

#### JSONPath Query Examples

Given a JSON file `data/users.json`:
```json
{
    "users": [
        {"id": 1, "name": "Alice", "role": "admin", "posts": 25},
        {"id": 2, "name": "Bob", "role": "editor", "posts": 12},
        {"id": 3, "name": "Carol", "role": "admin", "posts": 8}
    ],
    "settings": {
        "theme": "dark",
        "features": ["comments", "analytics"]
    }
}
```

You can query this data using JSONPath expressions:

```json
{
    "sources": {
        "users_data": {
            "type": "json",
            "path": "data/users.json"
        }
    },
    "queries": [
        {
            "name": "all_users",
            "source": "users_data",
            "query": "$.users[*]"
        },
        {
            "name": "admin_users",
            "source": "users_data", 
            "query": "$.users[?(@.role == 'admin')]"
        },
        {
            "name": "user_names",
            "source": "users_data",
            "query": "$.users[*].name"
        },
        {
            "name": "active_users",
            "source": "users_data",
            "query": "$.users[?(@.posts > 10)]"
        }
    ]
}
```

#### JSONPath Syntax Reference

- `$` - Root element
- `.` - Child element
- `[*]` - All array elements
- `[0]` - First array element
- `[?(@.field == 'value')]` - Filter expression
- `..field` - Recursive descent (find field anywhere)
- `[start:end]` - Array slice

#### Simple Dot Notation

For basic access, you can also use simple dot notation:

```json
{
    "name": "theme_setting",
    "source": "users_data",
    "query": "settings.theme"
}
```

### Adding Queries

Define queries to load data from your sources:

```json
{
    "sources": {
        "blog_db": {
            "type": "sqlite",
            "path": "data/blog.db"
        }
    },
    "queries": [
        {
            "name": "recent_posts",
            "source": "blog_db",
            "query": "SELECT title, slug, date, excerpt FROM posts ORDER BY date DESC LIMIT 5"
        },
        {
            "name": "categories",
            "source": "blog_db", 
            "query": "SELECT name, slug, COUNT(*) as post_count FROM categories JOIN posts ON categories.id = posts.category_id GROUP BY categories.id"
        }
    ]
}
```

### Using Query Data in Templates

Access query results through the `data.queries` object. This works for both SQL and JSON query results:

```html
<section class="recent-posts">
    <h2>Recent Posts</h2>
    {% for post in data.queries.recent_posts %}
    <article>
        <h3><a href="/posts/{{ post.slug }}">{{ post.title }}</a></h3>
        <time>{{ post.date | date_format('%B %d, %Y') }}</time>
        <p>{{ post.excerpt }}</p>
    </article>
    {% endfor %}
</section>

<aside class="categories">
    <h3>Categories</h3>
    <ul>
    {% for category in data.queries.categories %}
        <li><a href="/category/{{ category.slug }}">{{ category.name }} ({{ category.post_count }})</a></li>
    {% endfor %}
    </ul>
</aside>
```

#### Using JSON Query Results

For JSON data queries, access the results similarly:

```html
<section class="users">
    <h2>Admin Users</h2>
    {% for user in data.queries.admin_users %}
    <div class="user-card">
        <h3>{{ user.name }}</h3>
        <p>Role: {{ user.role }}</p>
        <p>Posts: {{ user.posts }}</p>
    </div>
    {% endfor %}
</section>

<div class="site-theme">
    Current theme: {{ data.queries.theme_setting.value }}
</div>
```

### Page-Level Queries

You can also define queries in individual Markdown files:

```markdown
---
title: "Author Profile"
queries:
    author_posts:
        source: "blog_db"
        query: "SELECT title, slug, date FROM posts WHERE author_id = {{ author_id }} ORDER BY date DESC"
variables:
    author_id: 123
---

# {{ author.name }}

## Recent Posts by This Author

{% for post in data.page_queries.author_posts %}
- [{{ post.title }}](/posts/{{ post.slug }}) - {{ post.date | date_format('%Y-%m-%d') }}
{% endfor %}
```

The above example shows how to define a query that fetches posts by a specific author using the `author_id` variable.

## Generating Pages

The most powerful feature of Presskit is generating multiple pages from database queries.

### Generator Queries

Mark a query as a generator to create multiple pages:

```json
{
    "queries": [
        {
            "name": "blog_posts",
            "source": "blog_db",
            "query": "SELECT title, slug, content, date, author FROM posts WHERE published = 1",
            "generator": true,
            "template": "post",
            "output_path": "posts/#{slug}"
        }
    ]
}
```

### Generator Configuration

- `generator: true` - Marks this as a page generator
- `template` - Template to use for generated pages
- `output_path` - Path pattern with placeholders like `#{field_name}`

### Creating Generator Templates

Create a template for your generated pages (`templates/post.html`):

```html
<!DOCTYPE html>
<html>
<head>
    <title>{{ title }} | {{ site.title }}</title>
</head>
<body>
    <article>
        <h1>{{ title }}</h1>
        <time>{{ date | date_format('%B %d, %Y') }}</time>
        <div class="content">
            {{ content | safe }}
        </div>
        <p>By {{ author }}</p>
    </article>
    
    <nav>
        <a href="/">← Back to Home</a>
    </nav>
</body>
</html>
```

### Nested Queries

You can create parent-child query relationships:

```json
{
    "queries": [
        {
            "name": "authors",
            "source": "blog_db", 
            "query": "SELECT id, name, bio, slug FROM authors"
        },
        {
            "name": "authors.posts",
            "source": "blog_db",
            "query": "SELECT title, slug, date FROM posts WHERE author_id = {{ id }} ORDER BY date DESC"
        }
    ]
}
```

Access nested data in templates:

```html
{% for author in data.queries.authors %}
<div class="author">
    <h2>{{ author.name }}</h2>
    <p>{{ author.bio }}</p>
    
    <h3>Posts by {{ author.name }}</h3>
    {% for post in author.posts %}
    <p><a href="/posts/{{ post.slug }}">{{ post.title }}</a> - {{ post.date }}</p>
    {% endfor %}
</div>
{% endfor %}
```

## Commands

### Build Commands

```bash
# Execute queries and cache results
presskit data

# Build entire site
presskit build

# Build specific file
presskit build content/about.md

# Generate pages from generator queries  
presskit generate

# Check query cache status
presskit status
```

Run `data` command before `build` or `generate` to ensure all queries are executed and data is cached.

### Development

```bash
# Start development server
presskit server

# Clean build artifacts
presskit clean
```

## Environment Variables

Presskit supports environment variables throughout your configuration using the `env:` prefix. This is essential for keeping sensitive data like database passwords out of your configuration files.

### Using Environment Variables

Any string value in your `presskit.json` can reference an environment variable:

```json
{
    "title": "env:SITE_TITLE",
    "url": "env:SITE_URL",
    "sources": {
        "database": {
            "type": "postgresql",
            "host": "env:DB_HOST",
            "port": "env:DB_PORT", 
            "database": "env:DB_NAME",
            "username": "env:DB_USER",
            "password": "env:DB_PASSWORD"
        }
    },
    "queries": [
        {
            "name": "posts",
            "source": "database",
            "query": "env:POSTS_QUERY"
        }
    ]
}
```

### Path Variables

Environment variables in paths support both `${VAR}` and `$VAR` syntax:

```json
{
    "sources": {
        "data": {
            "type": "sqlite",
            "path": "${HOME}/data/blog.db"
        }
    }
}
```

### Setting Environment Variables

```bash
# In your shell or .env file
export DB_PASSWORD="your-secure-password"
export SITE_URL="https://yoursite.com"
export DB_HOST="localhost"

# Run presskit
presskit build
```

## Advanced Configuration

### Full Configuration Example

```json
{
    "title": "My Blog",
    "description": "A blog about web development",
    "author": "Jane Developer", 
    "url": "env:SITE_URL",
    "version": "2.1.0",
    "language": "en-US",
    
    "content_dir": "content",
    "templates_dir": "templates", 
    "output_dir": "public",
    "cache_dir": ".cache",
    
    "default_template": "page",
    "markdown_extension": "md",
    "workers": "env:BUILD_WORKERS",
    
    "server_host": "0.0.0.0",
    "server_port": "env:PORT",
    
    "sources": {
        "blog_db": {
            "type": "postgresql",
            "host": "env:DB_HOST",
            "port": 5432,
            "database": "env:DB_NAME",
            "username": "env:DB_USER",
            "password": "env:DB_PASSWORD",
            "options": {
                "pool_min_size": 2,
                "pool_max_size": 10
            }
        },
        "analytics": {
            "type": "duckdb",
            "path": "data/analytics.duckdb"
        },
        "config": {
            "type": "json",
            "path": "${CONFIG_DIR}/site-config.json"
        }
    },
    
    "default_source": "blog_db",
    
    "variables": {
        "environment": "env:ENVIRONMENT",
        "analytics_id": "env:ANALYTICS_ID"
    },
    
    "queries": [
        {
            "name": "posts",
            "source": "blog_db",
            "query": "SELECT * FROM posts WHERE status = 'published' ORDER BY date DESC",
            "generator": true,
            "template": "post", 
            "output_path": "blog/#{slug}"
        },
        {
            "name": "recent_posts",
            "source": "blog_db",
            "query": "SELECT title, slug, excerpt, date FROM posts WHERE status = 'published' ORDER BY date DESC LIMIT 5"
        },
        {
            "name": "page_views",
            "source": "analytics",
            "query": "SELECT page, views FROM page_stats WHERE date >= current_date - interval '30 days'"
        }
    ]
}
```

### Custom Filters and Functions

Presskit includes useful Jinja2 filters and functions:

#### Filters

- `date_format(format)` - Format dates from YYYY-MM-DD to any format
  ```html
  {{ "2024-01-15" | date_format('%B %d, %Y') }}
  <!-- Output: January 15, 2024 -->
  ```

- `flatten` - Flatten a list of lists into a single list
  ```html
  {{ [[1, 2], [3, 4]] | flatten }}
  <!-- Output: [1, 2, 3, 4] -->
  ```

- `stringify(sep=" ")` - Convert a value or list of values into a string
  ```html
  {{ ["apple", "banana", "cherry"] | stringify(", ") }}
  <!-- Output: apple, banana, cherry -->
  ```

- `is_truthy` - Check if a value is truthy
  ```html
  {% if post.featured | is_truthy %}
  <span class="featured">Featured</span>
  {% endif %}
  ```

- `slugify(allow_unicode=False, sep="-")` - Convert a string to a URL-friendly slug
  ```html
  {{ "Hello World!" | slugify }}
  <!-- Output: hello-world -->
  ```

- `plainify` - Remove all HTML tags from a string
  ```html
  {{ "<p>Hello <strong>world</strong></p>" | plainify }}
  <!-- Output: Hello world -->
  ```

- `jsonify(**kwargs)` - Convert an object to a JSON string
  ```html
  {{ {"name": "John", "age": 30} | jsonify }}
  <!-- Output: {"name": "John", "age": 30} -->
  ```

- `humanize` - Convert a number to a human-readable string
  ```html
  {{ 1234567 | humanize }}
  <!-- Output: 1.23M -->
  ```

#### Functions

- `short_random_id(prefix="", k=8, seed=None)` - Generate a random ID with optional prefix
  ```html
  <div id="{{ short_random_id() }}">Random ID</div>
  <!-- Output: <div id="a7b2c4d8">Random ID</div> -->
  
  <button id="{{ short_random_id('btn-') }}">Click me</button>
  <!-- Output: <button id="btn-x9y4z2w1">Click me</button> -->
  
  <input id="{{ short_random_id('input-', 12) }}">
  <!-- Output: <input id="input-m5n8p3q7r2s6"> -->
  ```

- `template_debug()` - Display all available template variables in a formatted, collapsible HTML structure
  ```html
  <!-- Add this anywhere in your template for debugging -->
  {{ template_debug() }}
  ```
  
  This function generates a nicely formatted HTML panel showing all template variables organized by category (site, build, page, data, other). Perfect for debugging template issues or exploring what data is available in your templates.

## Changes

- 0.0.5 - Filters and functions for Jinja2 templates, new `template_debug()` function for debugging templates
- 0.0.4 - Bug fix for DuckDB data source to read relative paths correctly, DuckDB read-only mode, `--version` flag for CLI
- 0.0.3 - `--reload` flag on build and server commands to watch for file changes and rebuild automatically
- 0.0.2 - Extensible modular data sources, DuckDB, PostgreSQL, environment variables in configuration
- 0.0.1 - Initial version with site configuration, markdown processing, and Jinja templating

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "presskit",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.11",
    "maintainer_email": null,
    "keywords": "static site generator, static sites, ssg",
    "author": null,
    "author_email": "Asif Rahman <asiftr@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/e5/1b/2b333c6c0b54b433fd367010c45c26e6b6637128156c1a70dd624c13cc45/presskit-0.0.5.tar.gz",
    "platform": null,
    "description": "# Presskit\n\nA powerful static site generator that combines Markdown content with Jinja2 templating and database-driven page generation. Presskit lets you build dynamic static sites by connecting your content to SQLite databases and JSON data sources.\n\n## Key Features\n\n- **Jinja2 Templating**: Use Jinja2 variables and logic in both Markdown content and HTML layouts\n- **Multiple Data Sources**: Connect to SQLite, PostgreSQL, DuckDB databases, and JSON files with JSONPath querying\n- **Dynamic Page Generation**: Generate multiple pages automatically from database query results\n- **Structured Context**: Access site metadata, build information, and data through a clean template context\n\n## Installation\n\n```bash\npip install presskit\n```\n\nOr you can use [Astral's uv](https://docs.astral.sh/uv/) Python package manager to install Presskit as a self-contained tool so it can be run from the command line without needing to activate a virtual environment:\n\n```bash\nuv tool install presskit\n```\n\n### Database Dependencies\n\nPresskit supports different data sources. Install additional dependencies based on your needs:\n\n```bash\n# For PostgreSQL support\npip install presskit[postgresql]\n\n# For DuckDB support  \npip install presskit[duckdb]\n```\n\n## Quick Start\n\n1. Create a new site directory:\n```bash\nmkdir my-site\ncd my-site\n```\n\n2. Create the basic structure:\n```\nmy-site/\n\u251c\u2500\u2500 presskit.json      # Configuration file\n\u251c\u2500\u2500 content/           # Markdown files\n\u251c\u2500\u2500 templates/         # HTML templates\n\u2514\u2500\u2500 public/            # Generated output (created automatically)\n```\n\n3. Build your site:\n```bash\npresskit build\n```\n\n## Basic Usage\n\n### Writing Markdown Content\n\nCreate Markdown files in the `content/` directory. Each file can include YAML front matter for metadata:\n\n```\n---\ntitle: \"Welcome to My Site\"\ndescription: \"A brief introduction\"\nlayout: page\n---\n\n# Welcome\n\nThis is my **awesome** site built with Presskit!\n```\n\n### Creating HTML Templates\n\nTemplates go in the `templates/` directory. Here's a basic `page.html` template:\n\n```html\n<!DOCTYPE html>\n<html lang=\"{{ site.language }}\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>{{ page.title or site.title }}</title>\n    <meta name=\"description\" content=\"{{ page.description or site.description }}\">\n</head>\n<body>\n    <header>\n        <h1>{{ site.title }}</h1>\n    </header>\n    \n    <main>\n        {{ page.content }}\n    </main>\n    \n    <footer>\n        <p>&copy; {{ build.year }} {{ site.author }}</p>\n    </footer>\n</body>\n</html>\n```\n\n### Configuration\n\nCreate a `presskit.json` file to configure your site:\n\n```json\n{\n    \"title\": \"My Awesome Site\",\n    \"description\": \"A site built with Presskit\",\n    \"author\": \"Your Name\",\n    \"url\": \"https://mysite.com\",\n    \"language\": \"en-US\"\n}\n```\n\n## Template Variables\n\nPresskit provides a structured context with the following variables available in all templates:\n\n### Site Variables (`site.*`)\n- `site.title` - Site title\n- `site.description` - Site description  \n- `site.author` - Site author\n- `site.url` - Base site URL\n- `site.version` - Site version\n- `site.language` - Site language\n\n### Build Variables (`build.*`)\n- `build.date` - Build date (YYYY-MM-DD)\n- `build.year` - Build year\n- `build.timestamp` - Full build timestamp\n- `build.iso_date` - Build date in ISO format\n\n### Page Variables (`page.*`)\n- `page.filename` - Page filename without extension\n- `page.filepath` - Full file path\n- `page.path` - Clean URL path\n- `page.layout` - Template layout name\n- `page.content` - Processed HTML content (in templates)\n- `page.title` - Page title from front matter\n- `page.description` - Page description from front matter\n\n### Data Variables (`data.*`)\n- `data.queries` - Results from named queries\n- `data.sources` - JSON data sources\n- `data.page_queries` - Page-specific query results\n\nPlus any custom variables from your front matter are available at the top level.\n\n## Using Variables in Markdown\n\nYou can use Jinja2 templating directly in your Markdown content:\n\n```\n---\ntitle: About\ncategory: personal\n---\n\n# About {{ site.author }}\n\nThis site was built on {{ build.date }} and is currently version {{ site.version }}.\n\n{% if category == \"personal\" %}\nThis is a personal page about {{ site.author }}.\n{% endif %}\n```\n\n## Data Sources and Queries\n\nPresskit's data integration feature allows you to connect your static site to data sources, enabling content generation while maintaining the performance benefits of static sites. This powerful feature bridges the gap between static and dynamic websites.\n\nThis enables data-driven pages that display statistics, reports, or any structured data. Ideal for portfolios showcasing project metrics, business dashboards, or documentation sites pulling from APIs.\n\nThis encourages separation of concerns where you keep your content in databases where it can be easily edited, queried, and managed, while your site structure remains in version control.\n\n### Configuring Data Sources\n\nPresskit supports multiple data source types. Add them to your `presskit.json`:\n\n#### SQLite\n\n```json\n{\n    \"sources\": {\n        \"blog_db\": {\n            \"type\": \"sqlite\",\n            \"path\": \"data/blog.db\"\n        }\n    }\n}\n```\n\n#### PostgreSQL\n\n```json\n{\n    \"sources\": {\n        \"postgres_db\": {\n            \"type\": \"postgresql\", \n            \"host\": \"localhost\",\n            \"port\": 5432,\n            \"database\": \"mydb\",\n            \"username\": \"user\",\n            \"password\": \"env:DB_PASSWORD\"\n        }\n    }\n}\n```\n\n#### DuckDB\n\n```json\n{\n    \"sources\": {\n        \"analytics_db\": {\n            \"type\": \"duckdb\",\n            \"path\": \"data/analytics.duckdb\"\n        }\n    }\n}\n```\n\n#### JSON Files\n\n```json\n{\n    \"sources\": {\n        \"config\": {\n            \"type\": \"json\",\n            \"path\": \"data/site-config.json\"\n        }\n    }\n}\n```\n\nJSON sources support both basic data loading and advanced JSONPath querying for extracting specific data from complex JSON structures.\n\n#### Connection Strings\n\nYou can also use connection strings for database sources:\n\n```json\n{\n    \"sources\": {\n        \"prod_db\": {\n            \"type\": \"postgresql\",\n            \"connection_string\": \"env:DATABASE_URL\"\n        }\n    }\n}\n```\n\n### JSON Data Querying\n\nJSON sources support powerful JSONPath expressions for extracting data from complex JSON structures. JSONPath is a query language for JSON, similar to XPath for XML.\n\n#### JSONPath Query Examples\n\nGiven a JSON file `data/users.json`:\n```json\n{\n    \"users\": [\n        {\"id\": 1, \"name\": \"Alice\", \"role\": \"admin\", \"posts\": 25},\n        {\"id\": 2, \"name\": \"Bob\", \"role\": \"editor\", \"posts\": 12},\n        {\"id\": 3, \"name\": \"Carol\", \"role\": \"admin\", \"posts\": 8}\n    ],\n    \"settings\": {\n        \"theme\": \"dark\",\n        \"features\": [\"comments\", \"analytics\"]\n    }\n}\n```\n\nYou can query this data using JSONPath expressions:\n\n```json\n{\n    \"sources\": {\n        \"users_data\": {\n            \"type\": \"json\",\n            \"path\": \"data/users.json\"\n        }\n    },\n    \"queries\": [\n        {\n            \"name\": \"all_users\",\n            \"source\": \"users_data\",\n            \"query\": \"$.users[*]\"\n        },\n        {\n            \"name\": \"admin_users\",\n            \"source\": \"users_data\", \n            \"query\": \"$.users[?(@.role == 'admin')]\"\n        },\n        {\n            \"name\": \"user_names\",\n            \"source\": \"users_data\",\n            \"query\": \"$.users[*].name\"\n        },\n        {\n            \"name\": \"active_users\",\n            \"source\": \"users_data\",\n            \"query\": \"$.users[?(@.posts > 10)]\"\n        }\n    ]\n}\n```\n\n#### JSONPath Syntax Reference\n\n- `$` - Root element\n- `.` - Child element\n- `[*]` - All array elements\n- `[0]` - First array element\n- `[?(@.field == 'value')]` - Filter expression\n- `..field` - Recursive descent (find field anywhere)\n- `[start:end]` - Array slice\n\n#### Simple Dot Notation\n\nFor basic access, you can also use simple dot notation:\n\n```json\n{\n    \"name\": \"theme_setting\",\n    \"source\": \"users_data\",\n    \"query\": \"settings.theme\"\n}\n```\n\n### Adding Queries\n\nDefine queries to load data from your sources:\n\n```json\n{\n    \"sources\": {\n        \"blog_db\": {\n            \"type\": \"sqlite\",\n            \"path\": \"data/blog.db\"\n        }\n    },\n    \"queries\": [\n        {\n            \"name\": \"recent_posts\",\n            \"source\": \"blog_db\",\n            \"query\": \"SELECT title, slug, date, excerpt FROM posts ORDER BY date DESC LIMIT 5\"\n        },\n        {\n            \"name\": \"categories\",\n            \"source\": \"blog_db\", \n            \"query\": \"SELECT name, slug, COUNT(*) as post_count FROM categories JOIN posts ON categories.id = posts.category_id GROUP BY categories.id\"\n        }\n    ]\n}\n```\n\n### Using Query Data in Templates\n\nAccess query results through the `data.queries` object. This works for both SQL and JSON query results:\n\n```html\n<section class=\"recent-posts\">\n    <h2>Recent Posts</h2>\n    {% for post in data.queries.recent_posts %}\n    <article>\n        <h3><a href=\"/posts/{{ post.slug }}\">{{ post.title }}</a></h3>\n        <time>{{ post.date | date_format('%B %d, %Y') }}</time>\n        <p>{{ post.excerpt }}</p>\n    </article>\n    {% endfor %}\n</section>\n\n<aside class=\"categories\">\n    <h3>Categories</h3>\n    <ul>\n    {% for category in data.queries.categories %}\n        <li><a href=\"/category/{{ category.slug }}\">{{ category.name }} ({{ category.post_count }})</a></li>\n    {% endfor %}\n    </ul>\n</aside>\n```\n\n#### Using JSON Query Results\n\nFor JSON data queries, access the results similarly:\n\n```html\n<section class=\"users\">\n    <h2>Admin Users</h2>\n    {% for user in data.queries.admin_users %}\n    <div class=\"user-card\">\n        <h3>{{ user.name }}</h3>\n        <p>Role: {{ user.role }}</p>\n        <p>Posts: {{ user.posts }}</p>\n    </div>\n    {% endfor %}\n</section>\n\n<div class=\"site-theme\">\n    Current theme: {{ data.queries.theme_setting.value }}\n</div>\n```\n\n### Page-Level Queries\n\nYou can also define queries in individual Markdown files:\n\n```markdown\n---\ntitle: \"Author Profile\"\nqueries:\n    author_posts:\n        source: \"blog_db\"\n        query: \"SELECT title, slug, date FROM posts WHERE author_id = {{ author_id }} ORDER BY date DESC\"\nvariables:\n    author_id: 123\n---\n\n# {{ author.name }}\n\n## Recent Posts by This Author\n\n{% for post in data.page_queries.author_posts %}\n- [{{ post.title }}](/posts/{{ post.slug }}) - {{ post.date | date_format('%Y-%m-%d') }}\n{% endfor %}\n```\n\nThe above example shows how to define a query that fetches posts by a specific author using the `author_id` variable.\n\n## Generating Pages\n\nThe most powerful feature of Presskit is generating multiple pages from database queries.\n\n### Generator Queries\n\nMark a query as a generator to create multiple pages:\n\n```json\n{\n    \"queries\": [\n        {\n            \"name\": \"blog_posts\",\n            \"source\": \"blog_db\",\n            \"query\": \"SELECT title, slug, content, date, author FROM posts WHERE published = 1\",\n            \"generator\": true,\n            \"template\": \"post\",\n            \"output_path\": \"posts/#{slug}\"\n        }\n    ]\n}\n```\n\n### Generator Configuration\n\n- `generator: true` - Marks this as a page generator\n- `template` - Template to use for generated pages\n- `output_path` - Path pattern with placeholders like `#{field_name}`\n\n### Creating Generator Templates\n\nCreate a template for your generated pages (`templates/post.html`):\n\n```html\n<!DOCTYPE html>\n<html>\n<head>\n    <title>{{ title }} | {{ site.title }}</title>\n</head>\n<body>\n    <article>\n        <h1>{{ title }}</h1>\n        <time>{{ date | date_format('%B %d, %Y') }}</time>\n        <div class=\"content\">\n            {{ content | safe }}\n        </div>\n        <p>By {{ author }}</p>\n    </article>\n    \n    <nav>\n        <a href=\"/\">\u2190 Back to Home</a>\n    </nav>\n</body>\n</html>\n```\n\n### Nested Queries\n\nYou can create parent-child query relationships:\n\n```json\n{\n    \"queries\": [\n        {\n            \"name\": \"authors\",\n            \"source\": \"blog_db\", \n            \"query\": \"SELECT id, name, bio, slug FROM authors\"\n        },\n        {\n            \"name\": \"authors.posts\",\n            \"source\": \"blog_db\",\n            \"query\": \"SELECT title, slug, date FROM posts WHERE author_id = {{ id }} ORDER BY date DESC\"\n        }\n    ]\n}\n```\n\nAccess nested data in templates:\n\n```html\n{% for author in data.queries.authors %}\n<div class=\"author\">\n    <h2>{{ author.name }}</h2>\n    <p>{{ author.bio }}</p>\n    \n    <h3>Posts by {{ author.name }}</h3>\n    {% for post in author.posts %}\n    <p><a href=\"/posts/{{ post.slug }}\">{{ post.title }}</a> - {{ post.date }}</p>\n    {% endfor %}\n</div>\n{% endfor %}\n```\n\n## Commands\n\n### Build Commands\n\n```bash\n# Execute queries and cache results\npresskit data\n\n# Build entire site\npresskit build\n\n# Build specific file\npresskit build content/about.md\n\n# Generate pages from generator queries  \npresskit generate\n\n# Check query cache status\npresskit status\n```\n\nRun `data` command before `build` or `generate` to ensure all queries are executed and data is cached.\n\n### Development\n\n```bash\n# Start development server\npresskit server\n\n# Clean build artifacts\npresskit clean\n```\n\n## Environment Variables\n\nPresskit supports environment variables throughout your configuration using the `env:` prefix. This is essential for keeping sensitive data like database passwords out of your configuration files.\n\n### Using Environment Variables\n\nAny string value in your `presskit.json` can reference an environment variable:\n\n```json\n{\n    \"title\": \"env:SITE_TITLE\",\n    \"url\": \"env:SITE_URL\",\n    \"sources\": {\n        \"database\": {\n            \"type\": \"postgresql\",\n            \"host\": \"env:DB_HOST\",\n            \"port\": \"env:DB_PORT\", \n            \"database\": \"env:DB_NAME\",\n            \"username\": \"env:DB_USER\",\n            \"password\": \"env:DB_PASSWORD\"\n        }\n    },\n    \"queries\": [\n        {\n            \"name\": \"posts\",\n            \"source\": \"database\",\n            \"query\": \"env:POSTS_QUERY\"\n        }\n    ]\n}\n```\n\n### Path Variables\n\nEnvironment variables in paths support both `${VAR}` and `$VAR` syntax:\n\n```json\n{\n    \"sources\": {\n        \"data\": {\n            \"type\": \"sqlite\",\n            \"path\": \"${HOME}/data/blog.db\"\n        }\n    }\n}\n```\n\n### Setting Environment Variables\n\n```bash\n# In your shell or .env file\nexport DB_PASSWORD=\"your-secure-password\"\nexport SITE_URL=\"https://yoursite.com\"\nexport DB_HOST=\"localhost\"\n\n# Run presskit\npresskit build\n```\n\n## Advanced Configuration\n\n### Full Configuration Example\n\n```json\n{\n    \"title\": \"My Blog\",\n    \"description\": \"A blog about web development\",\n    \"author\": \"Jane Developer\", \n    \"url\": \"env:SITE_URL\",\n    \"version\": \"2.1.0\",\n    \"language\": \"en-US\",\n    \n    \"content_dir\": \"content\",\n    \"templates_dir\": \"templates\", \n    \"output_dir\": \"public\",\n    \"cache_dir\": \".cache\",\n    \n    \"default_template\": \"page\",\n    \"markdown_extension\": \"md\",\n    \"workers\": \"env:BUILD_WORKERS\",\n    \n    \"server_host\": \"0.0.0.0\",\n    \"server_port\": \"env:PORT\",\n    \n    \"sources\": {\n        \"blog_db\": {\n            \"type\": \"postgresql\",\n            \"host\": \"env:DB_HOST\",\n            \"port\": 5432,\n            \"database\": \"env:DB_NAME\",\n            \"username\": \"env:DB_USER\",\n            \"password\": \"env:DB_PASSWORD\",\n            \"options\": {\n                \"pool_min_size\": 2,\n                \"pool_max_size\": 10\n            }\n        },\n        \"analytics\": {\n            \"type\": \"duckdb\",\n            \"path\": \"data/analytics.duckdb\"\n        },\n        \"config\": {\n            \"type\": \"json\",\n            \"path\": \"${CONFIG_DIR}/site-config.json\"\n        }\n    },\n    \n    \"default_source\": \"blog_db\",\n    \n    \"variables\": {\n        \"environment\": \"env:ENVIRONMENT\",\n        \"analytics_id\": \"env:ANALYTICS_ID\"\n    },\n    \n    \"queries\": [\n        {\n            \"name\": \"posts\",\n            \"source\": \"blog_db\",\n            \"query\": \"SELECT * FROM posts WHERE status = 'published' ORDER BY date DESC\",\n            \"generator\": true,\n            \"template\": \"post\", \n            \"output_path\": \"blog/#{slug}\"\n        },\n        {\n            \"name\": \"recent_posts\",\n            \"source\": \"blog_db\",\n            \"query\": \"SELECT title, slug, excerpt, date FROM posts WHERE status = 'published' ORDER BY date DESC LIMIT 5\"\n        },\n        {\n            \"name\": \"page_views\",\n            \"source\": \"analytics\",\n            \"query\": \"SELECT page, views FROM page_stats WHERE date >= current_date - interval '30 days'\"\n        }\n    ]\n}\n```\n\n### Custom Filters and Functions\n\nPresskit includes useful Jinja2 filters and functions:\n\n#### Filters\n\n- `date_format(format)` - Format dates from YYYY-MM-DD to any format\n  ```html\n  {{ \"2024-01-15\" | date_format('%B %d, %Y') }}\n  <!-- Output: January 15, 2024 -->\n  ```\n\n- `flatten` - Flatten a list of lists into a single list\n  ```html\n  {{ [[1, 2], [3, 4]] | flatten }}\n  <!-- Output: [1, 2, 3, 4] -->\n  ```\n\n- `stringify(sep=\" \")` - Convert a value or list of values into a string\n  ```html\n  {{ [\"apple\", \"banana\", \"cherry\"] | stringify(\", \") }}\n  <!-- Output: apple, banana, cherry -->\n  ```\n\n- `is_truthy` - Check if a value is truthy\n  ```html\n  {% if post.featured | is_truthy %}\n  <span class=\"featured\">Featured</span>\n  {% endif %}\n  ```\n\n- `slugify(allow_unicode=False, sep=\"-\")` - Convert a string to a URL-friendly slug\n  ```html\n  {{ \"Hello World!\" | slugify }}\n  <!-- Output: hello-world -->\n  ```\n\n- `plainify` - Remove all HTML tags from a string\n  ```html\n  {{ \"<p>Hello <strong>world</strong></p>\" | plainify }}\n  <!-- Output: Hello world -->\n  ```\n\n- `jsonify(**kwargs)` - Convert an object to a JSON string\n  ```html\n  {{ {\"name\": \"John\", \"age\": 30} | jsonify }}\n  <!-- Output: {\"name\": \"John\", \"age\": 30} -->\n  ```\n\n- `humanize` - Convert a number to a human-readable string\n  ```html\n  {{ 1234567 | humanize }}\n  <!-- Output: 1.23M -->\n  ```\n\n#### Functions\n\n- `short_random_id(prefix=\"\", k=8, seed=None)` - Generate a random ID with optional prefix\n  ```html\n  <div id=\"{{ short_random_id() }}\">Random ID</div>\n  <!-- Output: <div id=\"a7b2c4d8\">Random ID</div> -->\n  \n  <button id=\"{{ short_random_id('btn-') }}\">Click me</button>\n  <!-- Output: <button id=\"btn-x9y4z2w1\">Click me</button> -->\n  \n  <input id=\"{{ short_random_id('input-', 12) }}\">\n  <!-- Output: <input id=\"input-m5n8p3q7r2s6\"> -->\n  ```\n\n- `template_debug()` - Display all available template variables in a formatted, collapsible HTML structure\n  ```html\n  <!-- Add this anywhere in your template for debugging -->\n  {{ template_debug() }}\n  ```\n  \n  This function generates a nicely formatted HTML panel showing all template variables organized by category (site, build, page, data, other). Perfect for debugging template issues or exploring what data is available in your templates.\n\n## Changes\n\n- 0.0.5 - Filters and functions for Jinja2 templates, new `template_debug()` function for debugging templates\n- 0.0.4 - Bug fix for DuckDB data source to read relative paths correctly, DuckDB read-only mode, `--version` flag for CLI\n- 0.0.3 - `--reload` flag on build and server commands to watch for file changes and rebuild automatically\n- 0.0.2 - Extensible modular data sources, DuckDB, PostgreSQL, environment variables in configuration\n- 0.0.1 - Initial version with site configuration, markdown processing, and Jinja templating\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Static site generator",
    "version": "0.0.5",
    "project_urls": {
        "Documentation": "https://github.com/asifr/presskit",
        "Homepage": "https://github.com/asifr/presskit",
        "Repository": "https://github.com/asifr/presskit"
    },
    "split_keywords": [
        "static site generator",
        " static sites",
        " ssg"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "e587a0f28bcb679401114957b39d44e170d8840f603cc28bf8ad82c7c45c9082",
                "md5": "bceb4ad455ab1dc8e0cf0eedec1b0cf5",
                "sha256": "4555308ff66daa3da3087d90e2d6e61c7afedc40bcec3b4b5d6433efdda5db84"
            },
            "downloads": -1,
            "filename": "presskit-0.0.5-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "bceb4ad455ab1dc8e0cf0eedec1b0cf5",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.11",
            "size": 46671,
            "upload_time": "2025-07-14T12:18:41",
            "upload_time_iso_8601": "2025-07-14T12:18:41.415495Z",
            "url": "https://files.pythonhosted.org/packages/e5/87/a0f28bcb679401114957b39d44e170d8840f603cc28bf8ad82c7c45c9082/presskit-0.0.5-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "e51b2b333c6c0b54b433fd367010c45c26e6b6637128156c1a70dd624c13cc45",
                "md5": "53abcace060aaa08f4bbde04286ec324",
                "sha256": "2fd179df4b3a33c9793b626fb73782a2814534220837758483c546dd53a1fbe5"
            },
            "downloads": -1,
            "filename": "presskit-0.0.5.tar.gz",
            "has_sig": false,
            "md5_digest": "53abcace060aaa08f4bbde04286ec324",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.11",
            "size": 54239,
            "upload_time": "2025-07-14T12:18:42",
            "upload_time_iso_8601": "2025-07-14T12:18:42.716798Z",
            "url": "https://files.pythonhosted.org/packages/e5/1b/2b333c6c0b54b433fd367010c45c26e6b6637128156c1a70dd624c13cc45/presskit-0.0.5.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-14 12:18:42",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "asifr",
    "github_project": "presskit",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "presskit"
}
        
Elapsed time: 1.13687s