Skip to content

Review entities

The pre-annotated notes now need a human check. In this tutorial, you write a view, a short Python function telling Metanno how to show the notes and their entities. Annotators then read each note with its entities highlighted, fix the wrong ones, add the missing ones and tick those they checked.

Before you start

Follow Pre-annotate first: this tutorial shows the entities it added to oncology.pret.

Write the view

Metanno looks for the Python files of views, called recipes, in the metanno-home/recipes folder, next to the metanno-home/annotations folder of your annotation files, at the root of the file browser. Create it:

from pathlib import Path

Path("~/metanno-home/recipes").expanduser().mkdir(parents=True, exist_ok=True)

then write the view in it:

%%writefile ~/metanno-home/recipes/review_entities.py
from pret_joy import Stack

from metanno import DataRelation, DataWidgetFactory

# Name and color of each entity label, in the text and on the toolbar buttons
LABELS = {
    "tumeur": {"name": "Tumeur", "color": "rgb(255,200,206)"},
    "tnm": {"name": "TNM", "color": "rgb(210,236,247)"},
}


def main(store):
    """
    Show each note with its entities highlighted, and a table to check them
    """
    # The factory builds widgets that edit the same Store and share
    # their selected rows. The relation limits the entities to those
    # of the selected note
    factory = DataWidgetFactory(
        store,
        relations=[DataRelation("notes", "entities", on="note_id")],
    )

    # One row per note, selecting a note chooses the text shown below.
    # store_key is the table of the Store to show, primary_key the column
    # identifying its rows, and fields the columns to show
    notes = factory.create_table_widget(
        store_key="notes",
        primary_key="note_id",
        fields=[
            {"key": "note_id", "kind": "text"},
            {"key": "department", "kind": "text", "filterable": True},
            {"key": "text", "kind": "text"},
        ],
        style={"height": "150px"},
    )

    # Text of the selected note with its entities highlighted, and a
    # toolbar of labels: select some text then click a label to add an
    # entity, or click an entity to edit it. The texts come from notes,
    # the entities and their begin and end columns from entities
    text, toolbar = factory.create_text_widget(
        store_text_key="notes",
        store_spans_key="entities",
        text_key="text",
        text_primary_key="note_id",
        spans_primary_key="entity_id",
        fields=[{"key": "label", "kind": "select", "editable": True, "options": list(LABELS)}],
        labels=LABELS,
    )

    # Entities of the selected note, label, negated and checked are
    # editable
    entities = factory.create_table_widget(
        store_key="entities",
        primary_key="entity_id",
        fields=[
            {"key": "text", "kind": "text"},
            {"key": "label", "kind": "select", "editable": True, "options": list(LABELS)},
            {"key": "negated", "kind": "boolean", "editable": True},
            {"key": "checked", "kind": "boolean", "editable": True},
        ],
        style={"height": "200px"},
    )

    # Widgets stacked from top to bottom
    return Stack(notes, toolbar, text, entities, spacing=1)

main receives the Store of the annotation file and returns what the tab shows. Metanno calls it each time the file is opened with this view.

Meet DataWidgetFactory

DataWidgetFactory is the helper behind every Metanno view, including the table view you used so far. Its create_*_widget methods build tables, texts and forms connected to the Store: an edit in any widget is saved to the file, and selecting a row in one widget updates the others. fields lists the columns each widget shows and which ones can be edited. Design your view goes through its other widgets and options.

Open the notes with it

from pret import load_store

from metanno import view_opener

store = load_store("~/metanno-home/annotations/oncology.pret")
view_opener(store, view="review_entities")

The menu of the opener now shows review_entities: it lists the recipes of metanno-home/recipes that can display this file, the way the opener of Annotate a table showed note_tnm. Click Open with Metanno, then review the notes:

  • select note 4 in the top table: its text shows Tumeur, cT3N1M0 and métastase highlighted, and the entities table lists them, with métastase negated
  • tick checked for each entity you agree with, and change the label of the ones that are wrong
  • select note 5: the pipeline missed lésion, since it is not in its terms. Select the word in the text and click Tumeur in the toolbar to add it, then tick its negated box

As before, each change is saved to oncology.pret at once.

Reopen it from the sidebar

The file now remembers its view. Click oncology.pret under Annotations in the sidebar: it reopens with review_entities, without any notebook, which is how annotators usually work.

The tab toolbar
Button What it does
Kernel name Runs the view in another kernel, any kernel with Metanno installed can open a saved file
View menu Shows the file with another of your views
Reload Opens the file and runs the view again, after you changed the view
Open notebook Opens the notebook cell that opened the tab
Fullscreen Fills the browser tab with the view

JupyterLab reopens Metanno tabs after a page refresh. The URL of a fullscreen tab opens the same file again, for instance ?metanno-fullscreen=annotation:/home/you/metanno-home/annotations/oncology.pret, for anyone with access to your Jupyter server and the file.

Changing the view

Edit review_entities.py, then click the reload button of the tab toolbar: Metanno runs the view again on the same file, keeping all annotations.

Next

Your annotators can now work on this project. Design your view goes further, with panels, forms and keyboard shortcuts.