Metadata-Version: 2.4
Name: earlyseo-blog
Version: 1.0.5
Summary: Drop-in blog integration for Django & Flask — powered by EarlySEO. Articles are served from EarlySEO's CDN. Just add your site ID and go.
Project-URL: Homepage, https://earlyseo.com
Project-URL: Documentation, https://app.earlyseo.com/docs/python-sdk
Project-URL: Repository, https://github.com/earlyseo/blog-python
Author-email: EarlySEO <team@earlyseo.com>
License: Proprietary
License-File: LICENSE
Keywords: blog,cdn,cms,django,earlyseo,flask,seo
Classifier: Development Status :: 5 - Production/Stable
Classifier: Framework :: Django
Classifier: Framework :: Flask
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Requires-Python: >=3.9
Requires-Dist: httpx>=0.24
Requires-Dist: pydantic>=2.0
Provides-Extra: all
Requires-Dist: django>=4.2; extra == 'all'
Requires-Dist: flask>=2.3; extra == 'all'
Requires-Dist: httpx[http2]>=0.24; extra == 'all'
Requires-Dist: jinja2>=3.1; extra == 'all'
Provides-Extra: async
Requires-Dist: httpx[http2]>=0.24; extra == 'async'
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: respx>=0.20; extra == 'dev'
Provides-Extra: django
Requires-Dist: django>=4.2; extra == 'django'
Provides-Extra: flask
Requires-Dist: flask>=2.3; extra == 'flask'
Requires-Dist: jinja2>=3.1; extra == 'flask'
Description-Content-Type: text/markdown

# earlyseo-blog

Drop-in blog integration for **Django** and **Flask** — powered by [EarlySEO](https://earlyseo.com). Articles are authored in the EarlySEO dashboard and rendered in your Python web app.

## Quick Start (Recommended)

The fastest way to get started — run the CLI from your project root:

```bash
pip install earlyseo-blog
earlyseo-blog
```

The CLI automatically:

1. Detects whether you're using **Django** or **Flask**
2. Asks for your Site ID (from Dashboard → Integrations → SDK)
3. Generates starter templates, URL config, and a `.env` file

Run your dev server and visit `/blog` — your articles are live.

---

## Manual Setup

If you prefer to configure things yourself:

### Django

```bash
pip install "earlyseo-blog[django]"
```

**settings.py**

```python
INSTALLED_APPS = [
    # …
    "earlyseo_blog.django",
]

EARLYSEO_SITE_ID = "your-site-id"   # from Dashboard → Integrations → SDK
```

**urls.py**

```python
from django.urls import include, path

urlpatterns = [
    path("blog/", include("earlyseo_blog.django.urls")),
]
```

Visit `/blog` to see your articles.

#### Template tags

Override the built-in HTML by creating your own templates:

```html
<!-- templates/earlyseo/blog_list.html -->
{% extends "base.html" %}
{% load earlyseo %}

{% block extra_head %}{% earlyseo_styles %}{% endblock %}

{% block content %}
  {% for article in articles %}
    <h2><a href="{% url 'earlyseo_blog:blog_detail' slug=article.slug %}">{{ article.title }}</a></h2>
    <p>{{ article.meta_description }}</p>
  {% endfor %}
{% endblock %}
```

Available template tags: `{% earlyseo_styles %}`, `{% earlyseo_article_css %}`, `{% earlyseo_article article %}`.

### Flask

```bash
pip install "earlyseo-blog[flask]"
```

**app.py**

```python
from flask import Flask
from earlyseo_blog.flask.views import create_blog_blueprint

app = Flask(__name__)
app.config["EARLYSEO_SITE_ID"] = "your-site-id"

app.register_blueprint(create_blog_blueprint(), url_prefix="/blog")
```

Override the built-in HTML by placing `templates/earlyseo/blog_list.html` and `templates/earlyseo/blog_detail.html` in your templates folder.

---

## Sitemap

### Django

Plug into Django's built-in sitemaps framework:

```python
# settings.py
INSTALLED_APPS = [
    # …
    "django.contrib.sitemaps",
    "earlyseo_blog.django",
]
```

```python
# urls.py
from django.contrib.sitemaps.views import sitemap
from earlyseo_blog.django.sitemap import EarlySeoBlogSitemap

sitemaps = { "blog": EarlySeoBlogSitemap }

urlpatterns = [
    path("blog/", include("earlyseo_blog.django.urls")),
    path("sitemap.xml", sitemap, {"sitemaps": sitemaps}, name="sitemap"),
]
```

### Flask

Register a `/blog/sitemap.xml` route automatically:

```python
from earlyseo_blog.flask.sitemap import register_blog_sitemap

app.config["EARLYSEO_BASE_URL"] = "https://example.com"
register_blog_sitemap(app, url_prefix="/blog")
```

Or get entries for a custom sitemap:

```python
from earlyseo_blog.flask.sitemap import create_blog_sitemap_entries

entries = create_blog_sitemap_entries(base_url="https://example.com")
```

---

## Python Client

Use the client directly in any Python app:

```python
from earlyseo_blog import EarlySeoClient

client = EarlySeoClient(site_id="your-site-id")

# List articles (page 1)
page = client.get_list_page(1)
for article in page.articles:
    print(article.title, article.slug)

# Get a single article
article = client.get_article("my-article-slug")
print(article.title)
print(article.content_html)
```

### Async Client

```python
from earlyseo_blog import AsyncEarlySeoClient

async def main():
    async with AsyncEarlySeoClient(site_id="your-site-id") as client:
        page = await client.get_list_page(1)
        article = await client.get_article("my-article-slug")
```

Install the async extra for HTTP/2 support:

```bash
pip install "earlyseo-blog[async]"
```

---

## Models

All responses are validated with **Pydantic v2**:

| Model | Description |
|---|---|
| `Manifest` | Site manifest with total pages/articles |
| `ArticleListItem` | Summary for list views |
| `ArticleListPage` | Paginated list of `ArticleListItem` |
| `Article` | Full article with `content_html` |

```python
from earlyseo_blog.models import Article, Manifest
```

---

## CSS

Built-in styles for article content and blog layout:

```python
from earlyseo_blog.css import ARTICLE_CSS, BLOG_CSS
```

Include them in your `<style>` tag or template to get default styling.

---

## Configuration

| Setting | Description | Default |
|---|---|---|
| `site_id` | Your EarlySEO site ID (required) | — |

**Django**: Set `EARLYSEO_SITE_ID` in `settings.py`.

**Flask**: Set in `app.config["EARLYSEO_SITE_ID"]`.

---

## Requirements

- Python ≥ 3.9
- httpx ≥ 0.24
- pydantic ≥ 2.0
- Django ≥ 4.2 (for Django integration)
- Flask ≥ 2.3 (for Flask integration)

## License

Proprietary — see [LICENSE](./LICENSE) for details.
