About:Pharmacopedia.ext
Pharmacopedia extension — specification
Version: 0.7.10 · Requires: MediaWiki ≥ 1.45.0
Author: MDElliottMD · License: GPL-2.0-or-later
Source: /var/www/mediawiki/extensions/Pharmacopedia/
The Pharmacopedia extension turns a MediaWiki install into a structured, community-edited medicine reference. It adds parser tags, special pages, API modules, and a database schema that together support:
- Structured medicine pages via the About:Pharmacopedia.ext
Experience
No personal reports yet No clinical reports yetLog in to add your own experience.
Problems
No problems yet. Be the first to suggest one.
Titration strategies
No titration strategies yet. Be the first to suggest one.
Effects
No effects listed yet. Be the first to suggest one.
Relevant anecdote
No anecdotes yet. Share a relevant one.
Relevant Literature
No literature entries yet.
Log in to submit relevant literature.
SummaryCommon usesPharmacyPharmacologytemplate
- Per-user rating, voting, and reporting on effects, indications, titration strategies, anecdotes, and interactions
- Two-perspective data capture (personal vs. provider) wherever clinically meaningful
- Curated drug-class categories used as interaction endpoints
- A verified-provider role with document-based verification
High-level architecture
- Backend (PHP):
includes/— one class per parser tag, store, special page, or API module. Auto-loaded underMediaWiki\Extension\Pharmacopedia\. - Frontend (JS):
resources/ext.pharmacopedia.js— single IIFE binding click handlers, modals, and inline AJAX submits. - Styles (CSS):
resources/ext.pharmacopedia.css— shared row layout, per-tag chrome, dark-theme-friendly colors. - Schema:
sql/— ten core tables plus four migration patches. Picked up via theLoadExtensionSchemaUpdateshook.
Parser tags
All eight tags are registered via Hooks::onParserFirstCallInit:
| Tag | Purpose | Class |
|---|---|---|
<vote> |
Generic up/down binary vote on an arbitrary slug | VoteTag
|
<effect> |
Therapeutic or adverse effect, dual patient/provider perspectives | EffectTag
|
<discuss> |
Threaded comment widget | CommentTag
|
<effectsummary> |
Roll-up aggregate header | EffectSummaryTag
|
<titration> |
Titration strategy card with up/down vote | TitrationTag
|
<anecdote> |
Personal or provider story with up/down vote | AnecdoteTag
|
<indication> |
Condition the medicine is used for, 0–5 likert rating | IndicationTag
|
<pharmaInteractions/> |
Self-closing; renders the Interactions section for the current page | InteractionTag
|
All tags except <pharmaInteractions/> take a slug argument and (where relevant) a title, label, author, ref, or perspective.
Tag wikitext examples
<indication slug="ssri-depression" title="Major depressive disorder" author="MDElliottMD">Use cautiously in adolescents.</indication> <effect slug="nausea" label="Nausea"/> <effect ref="hyperkalemia"/> <!-- ref to global effect library --> <titration slug="slow-start-elderly" title="Slow start (elderly)" author="MDElliottMD">Begin at 10 mg q AM; titrate by 10 mg every 14 days.</titration> <anecdote slug="qi8sg2" perspective="provider" author="MDElliottMD">One patient developed serotonin syndrome at week 3...</anecdote> <pharmaInteractions/>
The unified compact row layout
Indication, Effect, Interaction, Titration, and Anecdote all render through a shared row pattern:
<div class="pcp-row pcp-row-{type} pcp-{type}" ...data-*>
<div class="pcp-row-head">
<span class="pcp-row-title">...</span>
<span class="pcp-row-aggs">...</span>
<span class="pcp-row-actions">
<button class="pcp-row-action pcp-row-action-toggle" data-target="rate">Rate</button>
[<button data-target="notes">Notes (N)</button>] # only for Interaction
[× delete button] # only for sysop/admin
</span>
</div>
[<div class="pcp-row-panel pcp-row-rate-panel" hidden>...</div>]
[<div class="pcp-row-panel pcp-row-notes-panel" hidden>...</div>]
[<div class="pcp-row-body">...wikitext body, always visible if present...</div>]
</div>
- Rate / Notes panels are hidden by default; the shared
.pcp-row-action-toggleJS handler reveals them inline on click. - Bodies (page-specific descriptions for Indication / Effect / Titration / Anecdote) are always visible.
- The
×admin delete button uses the established.pcp-del-btnpattern (or.pcp-ix-del-rowfor interactions). - Each row sits in its own block-formatting context (
display: flow-root) so its border-box respects floated infoboxes.
Voting / rating semantics
| Element | Scale | Perspectives | Storage |
|---|---|---|---|
| Vote tag | +1 / −1 binary | single | pcp_votes
|
| Titration | +1 / −1 binary | single | pcp_votes
|
| Anecdote | +1 / −1 binary | single (perspective is a metadata label, not a separate aggregate) | pcp_votes
|
| Indication | 0–5 likert + "don't know" | single | pcp_likert_reports
|
| Effect (patient) | experienced ∈ {yes, no, unsure} + valence −3..+3 | patient | pcp_effect_reports (perspective=1)
|
| Effect (provider) | frequency ∈ {0, 5, 20, 33, 50, 66, 80, 95, −1 don't know} + valence −3..+3 | provider | pcp_effect_reports (perspective=2)
|
| Interaction | experience 1–5 + outcome −3..+3 + optional free-text note | user + provider, separate aggregates | pcp_interaction_reports
|
Server-side aggregates: n, mean of the rating field, and (where applicable) severe = (vmean ≤ −2.5).
Aggregates are recomputed and returned by every report-submit API call so the row re-renders in place without a page reload.
Effect bucketing
When a wiki <ul> contains only <effect> cards, JavaScript groups them into buckets by the provider frequency mean (data-fmean):
| Bucket | fmean band | Default state |
|---|---|---|
| Common | > 20 | expanded, always visible |
| Uncommon | > 5 and ≤ 20 | collapsed |
| Rare | ≤ 5, provider vmean > −2.5 | collapsed |
| Rare but Severe | ≤ 5 and vmean ≤ −2.5 | expanded by default, red highlight |
| Not yet rated | no provider data (n=0) | collapsed, only renders if non-empty |
The vmean ≤ −2.5 threshold is also the trip-wire for the "severe" red treatment on interaction rows.
Interactions feature
The Interactions section is rendered by placing <pharmaInteractions/> anywhere in the wikitext of a medicine article (NS_MAIN) or a Category page (NS_CATEGORY).
Entity model
An interaction is an undirected edge between two endpoints. Each endpoint has a type (medicine or category) and a slug (DB-key form of the page title).
Pairs are stored in canonical order: smaller (type, slug) tuple on the left. This collapses A↔B and B↔A into a single row.
Tables
pcp_interactions: one row per interaction edge.
pi_id auto-increment pi_element_id FK -> pcp_votable_elements (reuse votes/comments infra) pi_left_type 'medicine' | 'category' pi_left_slug VARBINARY(255) pi_right_type 'medicine' | 'category' pi_right_slug VARBINARY(255) pi_created_user_id INT pi_created BINARY(14) UNIQUE (pi_left_type, pi_left_slug, pi_right_type, pi_right_slug)
pcp_interaction_reports: one row per (interaction, user, perspective).
pir_element_id FK -> pcp_votable_elements pir_user_id INT pir_perspective 1 = user, 2 = provider pir_experience TINYINT (1..5, nullable) pir_valence TINYINT (-3..+3, nullable) pir_note MEDIUMBLOB (nullable) pir_created BINARY(14) pir_updated BINARY(14) UNIQUE (pir_element_id, pir_user_id, pir_perspective)
Rendering rules
- On a medicine page M, list:
- Direct edges: rows where M is one side.
- Transitive edges: rows where one side is a category C that M is itself a member of (via MW's
categorylinks).
- Direct wins: if the same counterparty is reachable both directly and transitively, drop the transitive duplicate.
- On a Category page, list direct edges only (no transitive walk).
- Sort: pooled
valence_meanascending (most negative on top). Nulls sink. Tiebreakers:ndesc, then alphabetic. - Severe (any of pooled / user / provider vmean ≤ −2.5): red 4 px left border + red-tinted background + "severe" pill + counterparty title in red.
Add-interaction modal
Triggered by the + Add interaction button at the bottom of the section. Two-stage UX:
- Search input → results split into Medicines (teal chip) and Categories (amber chip).
- Click
Use→ confirm viaAdd interactionbutton → POST topharmacopediainteractionadd.
Categories appear in the modal only if tagged with the marker category (default Category:MedCategory, configurable via $wgPharmacopediaInteractionCategoryMarker). The transitive walk during rendering, however, uses all of a page's categories regardless of marker — historic data isn't hidden by changing the marker policy.
Delete
Two a