secubox-deb/packages/secubox-billets/api
CyberMind-FR f9302df075 feat(billets): emoji hashtags, quick views, and feed resumes
Authors already typed #hashtags in their bodies (37 of the 46 billets on gk2
carry one), so tags are EXTRACTED from the body rather than added via a picker:
tagging costs the author nothing and applies retroactively.

- migration 0004: tag(slug,emoji,label) + billet_tag, indexed on tag_slug.
  Extracted into rows rather than re-parsed per request, so filtering hits an
  index instead of LIKE-scanning every body. slug is accent-folded + lowercased
  (#Réseau == #reseau); emoji is STORED so a later curation change never
  silently restyles an already-published billet.
- services/tags.py: curated map + extraction. The lookbehind keeps a URL anchor
  (page#anchor) from becoming a tag; unknown tags fall back to a neutral badge
  rather than being rejected. Map covers this box's real content (podcast/media)
  as well as the security/box vocabulary; genuinely ambiguous topics are left on
  the default rather than editorialised.
- quick views: ?tag=slug on / and /feed.json, plus /tags.json and
  /admin/api/tags for the chip bar. Uses EXISTS, not a JOIN, so keyset paging
  cannot duplicate a row per matching tag; the pager carries the active tag.
- feed resumes: long billets show a short excerpt + "Lire la suite"; short ones
  render whole. `is_long` is measured on the markdown-collapsed text, so a short
  billet padded with link syntax is not "resumed" into a copy of itself.
- feed.json gains `summary` + `tags` (both JSON Feed 1.1 standard) with the
  emoji in the spec-legal `_secubox` extension; optional keys are omitted, not
  nulled.
- GET /admin/api/billets: the authoring list. The public feed cannot serve it —
  its item id is a permalink URL (so edit/delete could not address a row, which
  is why they 404'd) and it hides drafts, which an authoring client must see.
- manage.py backfill-tags: idempotent; also re-applies the curated map to
  existing tags as an explicit operator step.

Verified on gk2: backfill tagged 37/46 billets; ?tag=podcast→37, jfk→4, nope→0;
a draft's tag correctly stays out of the public chip bar; delete cascades.

Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-17 10:35:24 +02:00
..
migrations feat(billets): emoji hashtags, quick views, and feed resumes 2026-07-17 10:35:24 +02:00
routes feat(billets): emoji hashtags, quick views, and feed resumes 2026-07-17 10:35:24 +02:00
services feat(billets): emoji hashtags, quick views, and feed resumes 2026-07-17 10:35:24 +02:00
static feat(billets): emoji hashtags, quick views, and feed resumes 2026-07-17 10:35:24 +02:00
templates feat(billets): emoji hashtags, quick views, and feed resumes 2026-07-17 10:35:24 +02:00
__init__.py feat(billets): step 1 — data layer (schema, models, db, ULID, chained event log) 2026-07-11 14:52:22 +02:00
asgi.py feat(billets): step 7 — ASGI entrypoint, systemd/nginx/debian deploy, README 2026-07-11 15:52:08 +02:00
db.py feat(billets): SecuBox webui panel + navbar integration 2026-07-11 16:19:35 +02:00
ids.py feat(billets): step 1 — data layer (schema, models, db, ULID, chained event log) 2026-07-11 14:52:22 +02:00
main.py feat(billets): emoji hashtags, quick views, and feed resumes 2026-07-17 10:35:24 +02:00
manage.py feat(billets): emoji hashtags, quick views, and feed resumes 2026-07-17 10:35:24 +02:00
models.py feat(billets): communiqué style + embed snapshot vignette + portable HTML archive (ref #851) 2026-07-14 07:57:54 +02:00
repo.py feat(billets): emoji hashtags, quick views, and feed resumes 2026-07-17 10:35:24 +02:00
seed.py feat(billets): step 2 — public feed + permalinks + gitea revision store 2026-07-11 15:02:17 +02:00