Skip to main content

reflex-icd11ect

tests quality security PyPI Python Licence: MIT

A Reflex custom component for the WHO ICD-11 Embedded Classification Tools (ECT): the Embedded Coding Tool and the Embedded Browser, powered by the ICD-API.

It wraps @whoicd/icd11ect 1.8 and turns its imperative, page-global JavaScript API into ordinary Reflex components, props and event handlers.

import reflex as rx
from reflex_icd11ect import icd11ect


class State(rx.State):
    code: str = ""
    title: str = ""

    @rx.event
    def on_select(self, entity: dict[str, str]):
        self.code = entity["code"]
        self.title = entity["title"]


def index() -> rx.Component:
    return rx.vstack(
        rx.heading(f"{State.code} {State.title}"),
        icd11ect.coding_tool(
            api_server_url="http://localhost",   # your ICD-API deployment
            placeholder="Search the ICD-11...",
            on_select=State.on_select,
        ),
    )

Install

pip install reflex-icd11ect
# or
uv add reflex-icd11ect

The npm package and its stylesheet are installed into the frontend by Reflex; there is nothing to add to rxconfig.py.

What you get

Component Renders
icd11ect.coding_tool(...) Search box + result window, configured and bound
icd11ect.browser(...) The Embedded Browser
icd11ect.controller(...) Lifecycle only, renders nothing
icd11ect.provider(...) The controller used as page-wide settings
icd11ect.search_input(ino) The bare input.ctw-input element
icd11ect.result_window(ino) The bare div.ctw-window element
icd11ect.browser_window(ino) The bare div.ctw-eb-window element

Plus reflex_icd11ect.handler (the imperative ECT.Handler API as Reflex events), IcdTokenProvider (OAUTH 2.0 client credentials, backend side), SelectedEntity / BrowserContent (typed event payloads) and the constants (MMS_CHAPTERS, LANGUAGES, SOURCES, ...).

The classes are available too: Icd11ectCodingTool, Icd11ectBrowser, Icd11ectController.

Choosing a server

api_server_url is the only required setting.

Deployment Settings
Local ICD-API (Docker, Windows or systemd service) api_server_url="http://localhost", api_secured=False
WHO cloud API api_server_url="https://id.who.int", api_secured=True + a token
WHO developer test server (development only) api_server_url=WHO_DEVELOPER_TEST_API, api_secured=False
# a local deployment, no credentials, no data leaving your network
docker run -p 80:80 -e acceptLicense=true -e saveAnalytics=true whoicd/icd-api

Events

Prop ECT callback Payload
on_select selectedEntityFunction code, title, uri, linearization_uri, foundation_uri, selected_text, search_query, i_no
on_search_start searchStartedFunction none
on_search_end searchEndedFunction none
on_browser_load browserLoadedFunction none
on_browser_change browserChangedFunction code, uri, i_no
on_token_request getNewTokenFunction none

Payload keys are snake_case; wrap them for attribute access:

from reflex_icd11ect import SelectedEntity

@rx.event
def on_select(self, entity: dict[str, str]):
    selected = SelectedEntity.from_payload(entity)
    if selected.is_postcoordinated:
        ...

on_search_start / on_search_end carry no instance id in ECT, so with several instances on a page they are delivered to the instance whose search box has focus, falling back to the one that last emitted an event.

Driving the tool from the backend

from reflex_icd11ect import handler


class State(rx.State):
    @rx.event
    def code_fever(self):
        return handler.search("1", "fever")

    @rx.event
    def show_tuberculosis(self):
        return handler.set_browser_code("browser", "1B11")

    @rx.event
    def spanish(self):
        return handler.change_language("1", "es")

search, clear, set_browser_code, set_browser_uri, change_language, change_source, change_minor_version, overwrite_configuration, bind and set_token are available. They run entirely on the client, with no extra round trip.

OAUTH 2.0 with the WHO cloud API

Register at https://icd.who.int/icdapi for a client id and secret, then keep the secret on the backend:

from reflex_icd11ect import IcdTokenProvider, icd11ect

provider = IcdTokenProvider.from_env()   # ICD_CLIENT_ID / ICD_CLIENT_SECRET


class Auth(rx.State):
    token: str = ""

    @rx.event(background=True)
    async def refresh_token(self):
        token = await provider.async_token()
        async with self:
            self.token = token


icd11ect.coding_tool(
    api_server_url="https://id.who.int",
    api_secured=True,
    token=Auth.token,
    on_token_request=Auth.refresh_token,   # fired when the token expires
    on_select=State.on_select,
)

IcdTokenProvider caches the token and refreshes it five minutes before it expires. Only the short-lived token reaches the browser.

If you prefer the pattern WHO documents, where the browser fetches the token itself, set token_endpoint="/api/icd/token" (and token_field when the JSON field is not named token) instead of token.

Custom layouts

coding_tool is a controller with the two elements as children. Split them when the search box and the results belong in different places:

rx.fragment(
    icd11ect.controller(ino="dx", api_server_url=..., on_select=State.on_select),
    rx.hstack(
        rx.text("Diagnosis"),
        icd11ect.search_input("dx", placeholder="..."),
    ),
    rx.card(icd11ect.result_window("dx")),
)

The search box must stay uncontrolled: ECT writes into its value and disables it while it loads, so do not bind value to your state.

Several instances

ino is ECT's data-ctw-ino; it identifies an instance and must be unique on the page. ECT keeps a single configuration per page, so the component merges the settings of every mounted instance and applies what an instance sets differently through ECT.Handler.overwriteConfiguration:

icd11ect.coding_tool(ino="a", language="en", chapters_filter="")
icd11ect.coding_tool(ino="b", language="es", chapters_filter="06")
icd11ect.browser(ino="c", enable_select_button="categories")

Only part of ECT's settings can differ per instance: api_server_url, api_secured, source, minor_version, language, popup_mode, simplified_mode, disable_hierarchy, words_available, chapters_available, chapters_filter, subtrees_filter, flexisearch_available, search_by_code_or_uri, hierarchy_title and height (reflex_icd11ect.OVERWRITABLE_SETTINGS). The rest is page-wide and takes the value of the last instance that set it.

Settings

Every ECT setting is a prop, snake_case instead of camelCase. Props you do not set are not sent, so ECT keeps its own defaults; an empty string is treated the same way, which makes state-driven props easy.

Sharedapi_server_url, api_secured, source ("mms", "icf", "foundation"), minor_version, language, source_app, height, hierarchy_title, hierarchy_resizable, other_postcoordination, enable_keyboard, include_diagnostic_criteria, verbose.

Coding Toolpopup_mode, simplified_mode, disable_hierarchy, words_available, chapters_available, chapters_filter, subtrees_filter, flexisearch_available, search_by_code_or_uri, medical_coding_mode, view_selected_uri.

Embedded Browserenable_select_button ("none", "categories", "all", "allButRoot"), browser_search_available, browser_advanced_search_available, browser_hierarchy_available, browser_hierarchy_root_uris, browser_uri, display_other_foundation_children.

Every component accepts every setting: ECT keeps a single configuration per page, so a Coding Tool setting passed to a browser would still take effect on the page's Coding Tools, and hiding it would only mislead.

Bridge-only props: token, token_endpoint, token_field, token_timeout_ms, ino, plus placeholder, input_props and window_props for styling the elements.

docs/settings.md has the full table with ECT names and per-instance support.

How it works

ECT is not a React component: it renders itself into DOM nodes carrying data-ctw-ino, and keeps one global configuration and one set of callbacks for the whole page. The component therefore:

  • renders the markup ECT looks for, with your ino;
  • emits a page-level runtime (window.__reflexIcd11ect) that owns the ECT configuration and routes ECT's global callbacks to the right instance;
  • configures ECT with autoBind: false and calls ECT.Handler.bind(ino) from a useEffect, because ECT's auto-binding waits for window.onload, an event that has already fired by the time a single-page app renders a route;
  • re-applies settings and rebinds whenever your props change.

docs/architecture.md goes into detail.

Demo app

git clone https://github.com/ecrespo/reflex-icd11ect
cd reflex-icd11ect
uv pip install -e .
cd icd11ect_demo && uv run reflex run

Six pages: the Coding Tool with every setting live, the Embedded Browser, several instances side by side, custom layouts, server and OAUTH 2.0 handling, and a generated API reference.

Documentation

Requirements

  • Python >= 3.10, Reflex >= 0.9
  • An ICD-API server (local deployment or the WHO cloud API)

Licence

MIT for this component. The ICD-11 and the Embedded Classification Tools are published by the World Health Organization under their own licence: see https://icd.who.int/en/docs/icd11-license.pdf and the ICD-API terms.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

reflex_icd11ect-0.1.0.tar.gz (37.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

reflex_icd11ect-0.1.0-py3-none-any.whl (35.2 kB view details)

Uploaded Python 3

File details

Details for the file reflex_icd11ect-0.1.0.tar.gz.

File metadata

  • Download URL: reflex_icd11ect-0.1.0.tar.gz
  • Upload date:
  • Size: 37.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for reflex_icd11ect-0.1.0.tar.gz
Algorithm Hash digest
SHA256 fe5310727fd16655543e8103c0652ca5fca0d8669d100de94a3553c7087eabf6
MD5 e3852f02f4b5ec632217a6426693ea97
BLAKE2b-256 debeee637ff34c8c4940db1eb8dba81bdaab57c5ef4ec1a968efb4969af91393

See more details on using hashes here.

Provenance

The following attestation bundles were made for reflex_icd11ect-0.1.0.tar.gz:

Publisher: release.yml on ecrespo/reflex-icd11ect

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file reflex_icd11ect-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: reflex_icd11ect-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 35.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for reflex_icd11ect-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 85648f2890d061f9480701430bf010cd7e179f5693be0578eed62d63a26f5407
MD5 90d226edac9e7ecc92ea5a83dfe2c467
BLAKE2b-256 049f774f7c885bd2f0708b5cd4026b42bcc278ae77756e1a08bf38bc478ae435

See more details on using hashes here.

Provenance

The following attestation bundles were made for reflex_icd11ect-0.1.0-py3-none-any.whl:

Publisher: release.yml on ecrespo/reflex-icd11ect

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page