Metadata-Version: 2.4
Name: blog-package
Version: 0.1.0
Summary: Blog site package for the Core boilerplate (posts with a gallery, comments, likes, saves, share tracking). Depends on core-package for auth, RBAC, and the shared User model — it does not define its own.
Author-email: puzzllium <puzzllium@gmail.com>
Maintainer-email: puzzllium <puzzllium@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/puzzelchannel/package-blog
Project-URL: Issues, https://github.com/puzzelchannel/package-blog/issues
Classifier: Framework :: Django
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: core-package
Requires-Dist: django-ninja>=1.7.0
Requires-Dist: django-unfold>=0.81.0
Requires-Dist: Pillow>=10.0
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: pytest-django>=4.14; extra == "dev"
Requires-Dist: pytest-asyncio>=1.4; extra == "dev"
Dynamic: license-file

# blog-package

**Author:** puzzllium · [puzzllium@gmail.com](mailto:puzzllium@gmail.com) · [LinkedIn](https://www.linkedin.com/in/puzzllium)

Blog site package built on [core-package](https://pypi.org/project/core-package/):
posts with a cover picture and image gallery, comments, likes, saves (bookmarks)
and share-count tracking. It never defines its own user model: every post,
comment, like and save is owned by a core-package `User` via
`settings.AUTH_USER_MODEL`.

`Post.subject`/`description`/`text` are translatable through a single
`translations` JSON field (`{"fa": {"subject": "..."}, "de": {...}}`), so adding a
language needs no migration: `PUT /posts/{id}/translations/{language}`.

## Install

```bash
pip install blog-package
```

Requires Python 3.10+ and Django 5.2 (via core-package).

## What's inside

The package ships the reusable `blog` Django app:

| Module | Purpose |
| --- | --- |
| `blog.models` | Post, gallery image, comment, like, save |
| `blog.api` | Django Ninja router (public reads, logged-in interactions, admin-only writes) |
| `blog.schemas` / `blog.services` | Request/response schemas and business logic |
| `blog.admin` | Django admin (Unfold) incl. cover and gallery image management |

`testproject/` is dev-only scaffolding for this repo's own tests and is **not** part of the installable package.

## Dependencies

**Required**

| Package | Used for |
| --- | --- |
| core-package | Auth, RBAC, the shared `User` model, base/translatable models, pagination, throttling |
| django-ninja `>=1.7.0` | REST API layer |
| django-unfold `>=0.81.0` | Admin theme |
| Pillow `>=10.0` | Post cover and gallery images |

core-package in turn brings in Django 5.2 and the rest of the stack; see its README.

**Optional extras**

| Extra | Installs |
| --- | --- |
| `dev` | `pytest>=8`, `pytest-django>=4.14`, `pytest-asyncio>=1.4` |

## Local development

```bash
python -m venv .venv
source .venv/bin/activate
pip install -e /path/to/Core/Core   # core-package, editable
pip install -e ".[dev]"
pytest
```

## Installing into a Core-based project

1. Install it editable into the host repo's venv:
   ```bash
   source .venv/bin/activate
   pip install -e ../Packages/blog
   ```
2. Add it to `INSTALLED_APPS` in the host's `config/settings/base.py`, under
   the `# site packages` comment:
   ```python
   # site packages
   "blog",
   ```
3. Mount its router in the host's `config/api.py`:
   ```python
   from blog.api import router as blog_router
   ...
   api.add_router("/blog", blog_router)
   ```
4. Migrate:
   ```bash
   python manage.py migrate
   ```

**Installed automatically alongside `portfolio`**: `portfolio` declares
`blog-package` as a pip dependency (its site includes a blog section), so
`pip install -e ../Packages/portfolio` pulls this package in for you too —
but per Core's "no auto-discovery, explicit wiring" convention (§10), step 2
(`INSTALLED_APPS`) and step 3 (router mount) above still need doing by hand
for `blog`, exactly like for `portfolio` itself. The pip dependency only
saves you the `pip install` step, not the Django-level wiring.

## Endpoints

Mounted at whatever prefix the host chooses (`/blog` in the example above).
Reads are public; interactions require login; content writes are admin-only.

- `GET /posts` — paginated list of published posts (public), optional
  `?user_id=` and `?lang=` (returns the post's translated `subject`/
  `description`/`text` for that language if a translation exists, else the
  original)
- `GET /posts/by-slug/{slug}` — published post detail (public), optional
  `?lang=`
- `GET /posts/{post_id}/comments` — paginated comments on a post (public)
- `POST /posts/{post_id}/comments` — add a comment (logged in)
- `DELETE /comments/{comment_id}` — delete a comment (its author, or admin)
- `POST /posts/{post_id}/like` — toggle like on/off (logged in), returns the
  new state + like count
- `POST /posts/{post_id}/save` — toggle save/bookmark on/off (logged in)
- `POST /posts/{post_id}/share` — public, rate-limited by IP; increments and
  returns the post's share count (no account needed to share)
- `POST /posts`, `GET /posts/{post_id}`, `PATCH /posts/{post_id}`,
  `DELETE /posts/{post_id}` — admin-only (`require_role("admin")`, from Core)
- `PUT /posts/{post_id}/translations/{language}` — admin-only; sets/updates
  the translated `subject`/`description`/`text` for that language

Post pictures and gallery images are managed through the Django admin (same
convention as the portfolio package's `Project.cover_image`/`ProjectImage` —
no write API for images yet, deliberately, to avoid building endpoints
nothing needs yet).

## License

MIT. See [LICENSE](LICENSE).
