Hi -- I am currently using Prodigy for NER annotation, specifically utilizing the manual view where annotators can label span entities by clicking on them and assigning a label. I have a new requirement that I'd like to implement, and I'm seeking guidance on how to customize the interface to accommodate this need.
Requirement: When an annotator clicks on a span entity, I would like a text box to appear associated with that span, allowing the annotator to input additional data that will be linked to the selected span. If an annotator already added information in a text-box for a selected mention, it should be filled with that text on span clicking.
Queries:
How can I customize the interface to include this functionality? Do I need to incorporate additional JavaScript logic? If so, could you provide an example or guidance on how to implement this? (a starter Github repo would be super helpful)
Additionally, I would like to customize the click event behavior. Currently, when an entity is clicked, its label is removed. However, I aim to override this behavior so that clicking on an entity displays the text box for additional data input, and the label is only removed upon clicking an "X" icon.
I appreciate your assistance in helping me achieve these customizations efficiently. Please let me know if you require any further clarification or details.
Hi @Fatma and welcome! Out-of-the-box, we don't have a feature that does exactly what you're looking for, but I think it should be possible with some customizations, especially the text box for each spans.
Of course, a simple option would be to use a custom interface with blocks and a single text box. You could have a convention to add text for each spans on new lines in order and if your annotators stick to that, it would make it pretty easy to parse out the content later and associate it with the spans.
Alternatively, you could also focus on the entities first and then add a second step that streams in the examples with one entity per example and a text box for each. This is very straightforward and one advantage here could be that it reduces the cognitive load on the annotator because they only have to focus on one task at a time. And you're also able to review and potentially fix the annotated spans first, before going deeper. Because otherwise, any span labelling mistake will make both annotations invalid.
If you want the exact behaviour you described, you could also use JavaScript to add dynamic input fields as the spans change. I haven't had time to try this yet (I might be able to play with it later or over the weekend!) but it could work like this:
Add a html block to each card with an element you can use to render the inputs, e.g. <div id="inputs"></div>.
Listen to prodigyupdate event (called whenever task is updated) and check the window.prodigy.content.spans, an array of all highlighted spans in order.
Iterate over that and for each span, add an input field or textarea to the card and populate it with the value from a custom task field, e.g. window.prodigy.content.span1 if available, or however you want to save the texts added per span. This could also be an array of all span texts, in order.
Optional but probably helpful: when iterating over the spans, that also gives you access to all its meta information, so you could add a label to each field containing the span text, label and/or character offsets.
Add onChange handler to each text field that adds the respective text content to the task by calling window.prodigy.update, for example window.prodigy.update({ span1: event.target.value }). This updates the current example JSON with whatever is entered in the field.
This is a bit trickier and I can't immediately think of a perfect way to do it because the x icon is currently added alongside the label.
If you're okay with allowing a click on the label and the x icon, one possible (albeit hacky) option could be:
Add an event listener on both prodigymount and prodigyupdate to capture the original state and any changes to the entities.
Select all spans within the <mark> elements (highlighted entities), except the last (which is the label and x if span is hovered): document.querySelectorAll('mark > span:not(:last-child)').
Add an click event listener to each selected span that does nothing but event.preventDefault() and event.stopPropagation(), so it "captures" the click and doesn't propagate it to the parent, which would trigger the span deletion.
Again, I haven't tried it but I think it should work and I can look into it more later But I already wanted to update with my ideas so if you're a bit familiar with JavaScript, this can already give you something to work with and try out.