forked from retoor/devplacepy
Quiiz system
This commit is contained in:
@@ -43,6 +43,7 @@ Prefixes are wired in `main.py`:
|
||||
| `/xmlrpc` | xmlrpc.py - reverse-proxies XML-RPC calls to the forking XML-RPC bridge (`services/xmlrpc/`, supervised by `XmlrpcService` on loopback `config.XMLRPC_PORT`), which generates one XML-RPC method per documented REST endpoint from `docs_api.API_GROUPS` (`posts.create`, `feed.list`, ...). One struct of named params per call; auth via in-band `api_key`, `X-API-KEY`/`Bearer` header, or `http://user:pass@host/xmlrpc` Basic. Full introspection + `system.multicall`; REST errors become XML-RPC faults. Exempt from the rate limiter (enforced on the forwarded internal hop). See `devplacepy/services/xmlrpc/CLAUDE.md` |
|
||||
| `/api` | devrant/ package - devRant-compatible REST protocol (`auth.py`, `rants.py`, `comments.py`, `notifs.py`). Translates devRant requests onto DevPlace data: rants<->posts, comments/votes onto the native engagement layer, devRant integer ids onto each table's auto-increment `id`. Token auth via `devrant_tokens`. See `routers/devrant/CLAUDE.md` for the deep detail on this tree |
|
||||
| `/dbapi` | dbapi/ package - **primary-administrator-only, strictly READ-ONLY** generic database API over `dataset` (never inserts/updates/deletes/restores; the write surface was removed because it bypassed every per-route admin safeguard). `tables.py` (`GET /tables`, `GET /{table}/schema`), `crud.py` (read only: `GET /{table}` + `GET /{table}/{key}/{value}`, `?include_deleted`), `query.py` (`POST /query` hard SELECT-only validated read-only run; `POST /query/async` + `GET /query/{uid}` + `WS /query/{uid}/ws` via `DbApiJobService` kind `dbquery`), `nl.py` (`POST /nl` natural-language->SQL via the AI gateway, returns validated SELECT, `execute=true` runs it read-only). **Auth = the PRIMARY administrator only** (the earliest-created Admin, `utils.is_primary_admin` / `database.get_primary_admin_uid` - the same identity that gates backup downloads) by session/api_key (`services/dbapi/policy.py`); every other administrator is refused like a member, and there is **no internal-key path** - the gateway `internal_gateway_key()` is NOT accepted (no service-to-service access). SQL validated by `services/dbapi/validate.py` (sqlglot classify + suspicious-flag + read-only `EXPLAIN` dry-run). Devii tools `db_*` are read-only (list/get/query/nl; no write tools) and flagged `requires_primary_admin=True`, so they are added to the LLM tool list ONLY for the primary administrator (every other session never sees them and Devii is unaware they exist). `services/pubsub/policy.py` resolves its own admin/internal actor and does NOT reuse `dbapi.policy.caller_for`, so the primary-admin restriction does not leak onto the pub/sub bus. See `devplacepy/services/dbapi/CLAUDE.md` |
|
||||
| `/quizzes` | quizzes/ package - **Quizzes**: `index.py` (hub, create, import, detail, export, edit, delete, publish, per-quiz leaderboard, cross-quiz scoreboard), `questions.py` (builder page + question CRUD + reorder), `attempts.py` (start/resume, play, answer, finish, results). The hub `GET /quizzes` is the approved three-column `.feed-layout` reused verbatim from `/feed`: filters + search left (`?filter=all|todo|done|mine|drafts`, `?search=`, `?page=`), the quiz list centre with the **New quiz** and **Create quiz with Devii** actions, and the cross-quiz **scoreboard** rail right (score per user, best attempt per quiz, 15s display cache). Publishing is terminal - every write on a published quiz is a 400 and there is no unpublish route. See `devplacepy/services/quiz/CLAUDE.md` |
|
||||
| `/game` | game/ package - the **Code Farm** idle game (`index.py` + `farm.py`): `GET /game` (page), `GET /game/state`, `GET /game/leaderboard?board=`, `POST /game/{plant,harvest,buy-plot,upgrade,fertilize,daily,grant,perk,prestige,legacy,mastery,quests/claim}`, `POST /game/{defense/upgrade,defense/downgrade,infrastructure/buy,cosmetics/buy,cosmetics/equip}`, plus social `GET /game/farm/{username}` and `POST /game/farm/{username}/{water,steal}`. See `devplacepy/services/game/CLAUDE.md` |
|
||||
| (none) | push.py - web push + PWA: `GET`/`POST /push.json` (VAPID public key / subscription register), `GET /service-worker.js`, `GET /manifest.json` |
|
||||
| (none) | docs.py (docs/ package) - the documentation site (prose pages + API reference). See `routers/docs/CLAUDE.md` for the deep detail on this tree (`DOCS_PAGES`, audience tiers, prose rendering pipeline) |
|
||||
@@ -286,7 +287,8 @@ All SEO features are implemented across the following locations:
|
||||
## Engagement: reactions, bookmarks, polls, contribution heatmap
|
||||
|
||||
### Emoji reactions
|
||||
- Curated palette only: `REACTION_EMOJI` in `constants.py` (registered as a template global). `ReactionForm` rejects anything outside it; free-text emoji are not allowed.
|
||||
- **Any single emoji is allowed.** `ReactionForm` validates with `rendering.is_single_emoji(value)` (`emoji.emoji_count(text) == 1 and emoji.purely_emoji(text)`, whitespace stripped) - so every emoji the picker can emit (all 3953 fully-qualified sequences, skin tones and ZWJ families included) is accepted, while text, mixed text+emoji, and multi-emoji strings are rejected. `REACTION_EMOJI` in `constants.py` (a template global) is now only the **quick-pick palette** shown by default, not an allowlist; the full set comes from the vendored `emoji-picker-element` opened by the palette's `+` button.
|
||||
- The rendered chips are `reaction_emojis(_reactions)` (a `templating.py` global): the quick-pick palette plus any other emoji already used on that target (from `counts`/`mine`), so an off-palette reaction renders server-side too. `ReactionBar.js` creates a chip on the fly for any emoji returned by the JSON response that has none yet.
|
||||
- Endpoint `POST /reactions/{target_type}/{target_uid}` (`routers/reactions.py`) toggles one `(user, target, emoji)` row in the `reactions` table. Target types: `post`, `comment`, `gist`, `project`. AJAX (`x-requested-with: fetch`) returns `{counts, mine}`.
|
||||
- Reactions are **non-ranking** - they never touch `stars` or XP and intentionally send **no notifications** (votes already notify; reactions would be notification spam).
|
||||
- Batch reads via `get_reactions_by_targets(target_type, uids, user)` in `database.py` (used by feed, profile, comment loader, `load_detail`) - never per-row. The `_reaction_bar.html` partial takes `_type`, `_uid`, `_reactions` ({counts, mine}) and renders the full palette as toggle chips; `ReactionBar.js` uses document-level click delegation. All four engagement controllers (`ReactionBar`, `VoteManager`, `BookmarkManager`, `PollManager`) extend the shared `OptimisticAction` base (the `Http.sendForm -> render -> error` core); each keeps only its own event wiring and `_render`.
|
||||
|
||||
Reference in New Issue
Block a user