Skip to content

Sample labeling config for pausing annotators #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Apr 14, 2025
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
80a0e82
Merge pull request #2 from HumanSignal/fb-leap-1834/readable-pause-sc…
hlomzik Feb 24, 2025
ad426f9
Add sample labeling config
smohan123 Feb 25, 2025
aa463d4
feat: LEAP-1683: Custom scripts dropdown
nass600 Mar 19, 2025
545ae1b
Merge pull request #4 from HumanSignal/fb-leap-1683/custom-scripts
smohan123 Mar 20, 2025
565e25a
feat: LEAP-1683: Adapted structure and improved docs
nass600 Mar 21, 2025
54c21ca
Merge pull request #5 from HumanSignal/fb-leap-1683/custom-scripts
nass600 Mar 21, 2025
1fbd209
feat: LEAP-1683: Aded banner
nass600 Mar 21, 2025
7839fc4
feat: LEAP-1972: Ensure initial plugins are ready for release
nass600 Mar 27, 2025
8434ec9
Merge pull request #6 from HumanSignal/fb-leap-1972/ensure-initial-state
nass600 Mar 27, 2025
9cd38a7
docs: Edit manifest
caitlinwheeless Mar 28, 2025
2d465cf
Couple more fixes
caitlinwheeless Mar 28, 2025
4134e06
Incorporating Alec's comments
caitlinwheeless Mar 28, 2025
75fd2ab
Merge pull request #7 from HumanSignal/docs/update-manifest
nass600 Mar 31, 2025
4d71df7
fix: LEAP-1972: Minor improvements
nass600 Mar 31, 2025
5e8cbbd
Merge pull request #8 from HumanSignal/fb-leap-1972/ensure-initial-state
nass600 Mar 31, 2025
2e7f704
feat: LEAP-1984: Rename Custom Scripts to Plugins
nass600 Apr 4, 2025
3cbc49d
applied biome and changed ci
nass600 Apr 4, 2025
1d1cf96
standardized src folder
nass600 Apr 7, 2025
62a7511
biome fixes
nass600 Apr 7, 2025
719e8ee
fixed redact pii
nass600 Apr 7, 2025
1b083c7
Merge pull request #9 from HumanSignal/fb-leap-1984/rename-plugins
nass600 Apr 8, 2025
9a42248
Update README.md
nass600 Apr 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed .DS_Store
Binary file not shown.
16 changes: 16 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
## Description

<!-- A brief description of what this PR accomplishes. Include any screenshots if helpful. -->

## Related Issues

<!-- If this PR fixes an issue, link it here (e.g. Fixes #123). -->

## Checklist

- [ ] I have tested the changes in the Label Studio Labeling Config.
- [ ] I have added the `script.js` file.
- [ ] I have added the `view.xml` file.
- [ ] My code follows the guidelines stated in the README.md.

<!-- Thanks for contributing to Awesome Label Studio Configs! -->
22 changes: 22 additions & 0 deletions .github/workflows/validate-structure.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Validate Folder Structure

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
check-structure:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20

- name: Run Folder Structure Check
run: node validate-structure.mjs
74 changes: 73 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,73 @@
# label-studio-custom-scripts
![Label Studio Custom Scripts](docs/banner.png)

# Label Studio Custom Scripts

Welcome to **Label Studio Custom Scripts**! This repository contains `custom scripts` designed to extend the functionality of [Label Studio](https://labelstud.io), a powerful data labeling tool. These scripts can be used to automate workflows, integrate with external tools, and customize the labeling process according to your needs.

Whether you're building custom data processors, integrations, or UI components, you'll find the necessary resources and examples in this repo to get started.

## Official Documentation

For detailed documentation and guides on how to use and extend Label Studio with custom scripts, visit the official [Label Studio Scripts Documentation](https://docs.humansignal.com/guide/scripts).

## File Structure

This repository follows a clear folder structure to organize the various custom scripts and configuration files:

```bash
label-studio-custom-scripts/
├── custom-scripts/
│ ├── script1/
│ │ ├── data.{json|mp3|mp4}
│ │ ├── script.js
│ │ └── view.xml
│ ├── script2/
│ │ ├── data.{json|mp3|mp4}
│ │ ├── script.js
│ │ └── view.xml
│ └── ...
└── manifest.json
```

- **`/script1`**: Contains all the files to document a custom script.
- Each script has a `script.js` and `view.xml` file that define the logic and UI of the script.
- **`/script.js`**: Contains the actual custom script `javascript` file that can be embedded in the Label Studio code editor.
- **`/view.xml`**: Stores an example of a `<View>` that will work along the script.
- **`/data.{json|mp3|mp4}`**: Stores an example of the data that can be used along with the script.
- **`manifest.json`**: This file lists the scripts, their metadata (title, description, etc.), and their paths for easy integration with Label Studio.

## Usage

After your script gets merged you will be able to find it in your project's **Labeling Interface**

![Labeling Interface](docs/labeling-interface.png)

## Contributing

We welcome contributions! Whether it's bug fixes or new scripts, feel free to open a pull request. Here's how you can get started:

1. **Create a new branch** for your feature or bugfix.
2. **Make your changes** and ensure that they adhere to the project's file structure and guidelines. You need to create a folder with the name using underscores (`path`) of your script and add a `view.xml` and a `script.js` file minimum.
3. **Register the script** in the `manifest.json` adding the following information:
```json
[
{
"title": "Your script title",
"description": "Your script description",
"path": "exact_name_of_the_script_folder", // `script1` as per the File Structure example
"private": false // whether you want to hide it in the "Insert Script" dropdown in the Configurator code tab
}
]
```
4. **Test your changes** to ensure everything works as expected.
5. **Submit a pull request** explaining the changes you made.

Please make sure that your contributions follow the existing code style and structure.

## License

This software is licensed under the [Apache 2.0 LICENSE](/LICENSE) © [Heartex](https://www.heartex.com/). 2020-2025

---

If you have any questions or need assistance, feel free to reach out through issues or discussions. We look forward to your contributions!
10 changes: 0 additions & 10 deletions bulk_labeling/html.txt

This file was deleted.

Binary file removed count_words_in_textarea/.DS_Store
Binary file not shown.
22 changes: 0 additions & 22 deletions count_words_in_textarea/html.txt

This file was deleted.

File renamed without changes.
11 changes: 10 additions & 1 deletion bulk_labeling/js.txt → custom-scripts/bulk-labeling/script.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
/**
* Automatically creates all the text regions containing all instances of the selected text.
*/

// It will be triggered when a text selection happens
LSI.on('entityCreate', region => {
if (window.BULK_REGIONS) return;

window.BULK_REGIONS = true;
setTimeout(() => window.BULK_REGIONS = false, 1000);
console.log('matches', region.object._value.matchAll(region.text));

setTimeout(() => {
// Find all the text regions matching the selection
region.object._value.matchAll(new RegExp(region.text, "gi")).forEach(m => {
if (m.index === region.startOffset) return;

// Include them in the results as new selections
Htx.annotationStore.selected.createResult(
{ text: region.text, start: "/span[1]/text()[1]", startOffset: m.index, end: "/span[1]/text()[1]", endOffset: m.index + region.text.length },
{ labels: [...region.labeling.value.labels] },
Expand Down
23 changes: 23 additions & 0 deletions custom-scripts/bulk-labeling/view.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<View>
<Labels name="label" toName="text">
<Label value="PER" background="red"/>
<Label value="ORG" background="darkorange"/>
<Label value="LOC" background="orange"/>
<Label value="MISC" background="green"/>
</Labels>

<Text name="text" value="$text"/>
</View>

<!--
{
"data": {
"text": [
"Opossums, commonly known as possums in North America, are marsupials found primarily in the Americas. The most well-known species is the Virginia opossum (Didelphis virginiana), which ranges from Central America and the eastern United States to southern Canada. These adaptable creatures are known for their ability to thrive in a variety of environments, including both rural and urban areas. Opossums are also found in South America, where different species inhabit a range of ecosystems, from tropical rainforests to temperate forests.",
"Opossums are highly adaptable in terms of habitat, often residing in woodlands, farmland, and even suburban backyards. They typically seek shelter in hollow trees, abandoned burrows, or any dark, enclosed space they can find. Opossums are nocturnal and omnivorous, with a diet that includes fruits, insects, small animals, and even carrion. Their opportunistic feeding habits contribute to their resilience and ability to live in close proximity to human settlements.",
"In terms of behavior, opossums are solitary and nomadic, often moving to different locations in search of food. They are known for their unique defense mechanism of 'playing dead' or 'playing possum' when threatened, which involves mimicking the appearance and smell of a sick or dead animal to deter predators. Opossums have relatively short lifespans, typically living only 2 to 4 years in the wild. Despite their short lives, they reproduce quickly, with females giving birth to large litters of up to 20 young, although not all offspring typically survive to maturity.",
"In popular culture, opossums often appear as symbols of resilience and survival due to their hardy nature and ability to adapt to various environments. They are sometimes depicted in a comical or misunderstood light, given their nocturnal habits and somewhat disheveled appearance. Despite this, they play a crucial role in the ecosystem by controlling insect and rodent populations and cleaning up carrion. Opossums have been featured in various forms of media, from cartoons and children's books to movies, often emphasizing their unique behaviors and survival strategies."
]
}
}
-->
5 changes: 5 additions & 0 deletions custom-scripts/count-words-in-textarea/data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"data": {
"text": "The quick brown fox jumps over the lazy dog."
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
/**
* Validates the word count of the entered text to prevent submission if it exceeds a specified threshold
*/

let dismissed = false;

LSI.on("beforeSaveAnnotation", (store, annotation) => {
const textAreaResult = annotation.results.find(r => r.type === 'textarea' && r.from_name.name === 'textarea');

if (textAreaResult) {
words = textAreaResult.value.text[0]
word_count = words.split(" ").length;

if (word_count > 10) {
Htx.showModal("Word count is " + word_count + ". Please reduce to 10 or less.");
dismissed = true;
return false;
return false; // Block submission
}
}
return true;
});

return true; // Allow submission
});
13 changes: 13 additions & 0 deletions custom-scripts/count-words-in-textarea/view.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<View>
<Header value="Classify the text in less than 10 words"/>
<Text name="text" value="$text"/>
<TextArea name="textarea" toName="text" />
</View>

<!--
{
"data": {
"text": "The quick brown fox jumps over the lazy dog.",
}
}
-->
5 changes: 5 additions & 0 deletions custom-scripts/different-images-per-label/data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"data": {
"image": "/static/custom-scripts/custom-scripts/show_different_images_based_on_label_selected/img/demo-sample.png"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
/*
Objective:
Display different example check images at the bottom of the layout
depending on the class label selected
*/
/**
* Display different example check images at the bottom of the layout
* depending on the class label selected
*/

const IMG_ID = 'img_uniq'
// TODO: use your own keys and values here for label lookup and data objects to display
const imagesRoot = '/static/custom-scripts/custom-scripts/show_different_images_based_on_label_selected/img'
const images = {
'Addressee': '/storage-data/uploaded/?filepath=upload/86849/f7bce1e6-addressee.jpg',
'Account number': '/storage-data/uploaded/?filepath=upload/86849/f3fe2182-account-routing-number.png',
'Routing number': '/storage-data/uploaded/?filepath=upload/86849/f3fe2182-account-routing-number.png',
'Signature': '/storage-data/uploaded/?filepath=upload/86849/6926d68d-sign.jpg',
'Amount': '/storage-data/uploaded/?filepath=upload/86849/d8bf4fac-amount.jpg',
'Watermark': '/storage-data/uploaded/?filepath=upload/86849/695ba9b9-watermark.png',
'Date': '/storage-data/uploaded/?filepath=upload/86849/afc3193a-date.png',
'Correction': '/storage-data/uploaded/?filepath=upload/86849/d2c0218b-correction.jpg',
'Addressee': `${imagesRoot}/demo-addressee.jpg`,
'Account number': `${imagesRoot}/demo-routing-number.png`,
'Routing number': `${imagesRoot}/demo-routing-number.png`,
'Signature': `${imagesRoot}/demo-sign.jpg`,
'Amount': `${imagesRoot}/demo-amount.jpg`,
'Watermark': `${imagesRoot}/demo-watermark.png`,
'Date': `${imagesRoot}/demo-date.png`,
'Correction': `${imagesRoot}/demo-correction.jpg`,
}


function appendCheckImg() {
let imageEl = window[IMG_ID]
if (!imageEl) {
Expand All @@ -34,11 +33,12 @@ function appendCheckImg() {

// `label` is an actual tag name from config
const labels = LSI.annotation.names.get('label').children
// hardcoded access to rendered labels,
// if you will have more Labels in a future adjust the logic

// If you will have more Labels in a future adjust the logic
document.querySelectorAll('.lsf-label_clickable').forEach(
(lbl, index) => lbl.addEventListener('click', () => {
const src = images[labels[index].value]

// if there are no images with this key image will just have an empty src
imageEl.src = src
})
Expand Down
28 changes: 28 additions & 0 deletions custom-scripts/different-images-per-label/view.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<View>
<View>
<Image name="image" value="$image" />
<Labels name="label" toName="image">
<Label value="Addressee" background="gray" maxUsages="1" />
<Label value="Account number" background="magenta" maxUsages="1" />
<Label value="Routing number" background="green" maxUsages="1" />
<Label value="Signature" background="red" maxUsages="1" />
<Label value="Amount" background="orange" maxUsages="2" />
<Label value="Watermark" background="purple" />
<Label value="Date" background="brown" maxUsages="1" />
<Label value="Correction" background="black" maxUsages="1" />
</Labels>
<Rectangle name="bbox" toName="image" strokeWidth="3" />
</View>
<View>
<Header value="Example" />
<Header value="(Please make a selection)" size="5" />
</View>
</View>

<!--
{
"data": {
"image": "/static/custom-scripts/custom-scripts/show_different_images_based_on_label_selected/img/demo-sample.png"
}
}
-->
File renamed without changes.
Loading