Functions available:
this.allowSubmission(Boolean) this.changeFormValue(field_name, new_value) this.enableField(field_name, Boolean) this.disableField(field_name) this.mandatoryField(field_name, Boolean) this.changeInputValue(field_name, value) this.hideOutputField(["field_name1", "field_name2"],"template_name") this.unHideOutputField(["field_name1", "field_name2"],"template_name") this.hideInputField(["field_name1", "field_name2"],"template_name") this.unHideInputField(["field_name1", "field_name2"],"template_name") let value = JSON.stringify(finalValues) this.updateAnnotations(value) let errorMsgs = []if(errorMsgs.length > 0) { this.displayErrors(errorMsgs) this.disAllowSubmit(true) } else { this.disAllowSubmit(false) } this.changeAnnotationClasses(newClass); let inputText = inputValues[field_name] let currentUserId = localStorage.getItem("userId") if(currentUserLevel === 1) { this.changeFormValue(field_name, `${currentUserId}`) } For NER: let entities = _.get(annotations, "[0].value.annotations.entities", _.get(annotations, "annotations.entities", [])) To access form values in On-Change/on-Validation/On-save(also console the values coming from values and formValues then use as required): let fieldNameValue = values["field_name"] To access previous values in the on change code: (Useful to check if a particular field's value has changed') let preValues = oldValues["field_name"] To access annotations data in any annotation project let anns = annotations
Examples:
Audio transcription validation: No special characters allowed, tags validations, etc.
let regexNotToMatch = { "Add at least one space before the normal start tag.": /[^ ]<[^\/]*[^\/]*>/g, "Add at least one space after the normal end tag.": /<\/\s*[^>]*>[^ ]/g, "Add at least one space before and after a single tag.": /[^ ]<[^>]*\/>[^ ]|[^ ]<[^>]*\/>|<[^>]*\/>[^ ]/g, "Numbers are not allowed.": /[0-9]/g, "Special character: ^ ( ) { } [ ] ; : $ % * = are not allowed.": /[\^(){}\[\];:$%*=]/g, "New line/Enter is not allowed.": /\n/g, }; this.setState({ audioRegexNotToMatch: regexNotToMatch });
2. Generate 100 segments of transcription on load of a task:
let finalValues =[] let i=0 while(i < 100 ) { i = i + 1 finalValues.push( { "id": i, "curTime": 0, "endTime": i + 1, "startTime": i , "UIstartTime": i + 1, "UIendTime": i, "split": 0, "audiourl": "", "pos": 0, "transliterationData": "abc def ghi jxy lzg".split(" ").map(w => ({ "word": w, "tags": [], "languageTag": "", "editedText": "" })) }) } let x = JSON.stringify(finalValues) this.updateAnnotations(x)
3. Load classes for annotation projects using custom code:
let newClass = { Books: JSON.stringify({ "color": "#FFEBE8", "hotkey": "", "attributes": [ { "name": "Price", "fieldType": "Text", "mandatoryField": false, "possible_value": [ "" ], "default_value": "" } ] }), Car: JSON.stringify({ "color": "#FF000", "hotkey": "c", "attributes": [ { "name": "attr1", "fieldType": "DropDown", "mandatoryField": false, "possible_value": [ "attr1_v1", "attr1_v2" ], "default_value": "attr1_v1" }, { "name": "attr2", "fieldType": "DropDown", "mandatoryField": false, "possible_value": [ "attr2_v1", "attr2_v2" ], "default_value": "attr2_v1" } ] }), Quills: JSON.stringify({ "color": "#C23456", "hotkey": "q", "attributes": [ { "name": "Colour", "fieldType": "Text", "mandatoryField": false, "possible_value": [ "Blue", "Green", "Yellow", "Red", "White", "Orange" ], "default_value": "blue" } ] }), Tree: JSON.stringify({ "color": "#CCC", "hotkey": "t", "attributes": [ { "name": "attr1", "fieldType": "MultiDropDown", "mandatoryField": false, "possible_value": [ "attr1_v1", "attr1_v2" ], "default_value": "" }, { "name": "attr2", "fieldType": "Text", "mandatoryField": false, "possible_value": [ "" ], "default_value": "" } ] }), Van: JSON.stringify({ "color": "#F24110", "hotkey": "v", "attributes": [ { "name": "Vans", "fieldType": "DropDown", "mandatoryField": false, "possible_value": [ "Ferrari", "Kia", "MG" ], "default_value": "" } ] }) }; this.changeAnnotationClasses(newClass);
4. Add groups for image and video annotation using custom code:
let groupList = ["a", "b", "c", "d"] localStorage.setItem("groups", JSON.stringify(groupList))
5. Load pre-annotations and classes from YOLO data
let yoloData = inputValues["yolo-data"] // split yolo annotation data to array of individual anno data let re = /\b[\w.]+(?:[^\w\n]+[\w.]+){0,4}\b/g let arrYoloAnno = yoloData.match(re); /* Start: configure unique classes from custom code*/ // let yoloClasses = [] // arrYoloAnno.forEach(y => { // let currentClass = y.substr(0, y.indexOf(" ")) // if(!yoloClasses.includes(currentClass)){ // yoloClasses.push(currentClass) // } // }) // make class data // let newClass = {} // yoloClasses.forEach(c => { // let classValue = { // "color": '#'+(0x1000000+Math.random()*0xffffff).toString(16).substr(1,6), // random color // "hotkey": "", // "attributes": [] // } // newClass[c] = JSON.stringify(classValue) // }) // this.changeAnnotationClasses(newClass); /* End */ /* Class data*/ let newClass = { "0": "Person", "1": "Class1", "2": "Class2", "3": "Class3", "4": "Class4", "5": "Class5", "6": "Class6", "7": "Class7", "8": "Class8", "9": "Class9", "99": "Face" } /**/ /* Start: convert yolo data to TM anno format and setting it as pre anno */ /* The format of the YOLO label file is Label_ID_1 X_CENTER_NORM Y_CENTER_NORM WIDTH_NORM HEIGHT_NORM */ let preAnno = arrYoloAnno.map((singleYoloAnno, ind) => { let singleAnnoSplitData = singleYoloAnno.split(" ") let X_CENTER = parseFloat(singleAnnoSplitData[1]) let Y_CENTER = parseFloat(singleAnnoSplitData[2]) let WIDTH_NORM = parseFloat(singleAnnoSplitData[3])/2 let HEIGHT_NORM = parseFloat(singleAnnoSplitData[4])/2 let firstPoint = [X_CENTER - WIDTH_NORM, Y_CENTER - HEIGHT_NORM] let secondPoint = [X_CENTER + WIDTH_NORM, Y_CENTER - HEIGHT_NORM] let thirdPoint = [X_CENTER + WIDTH_NORM, Y_CENTER + HEIGHT_NORM] let fourthPoint = [X_CENTER - WIDTH_NORM, Y_CENTER + HEIGHT_NORM] return ({ "id":ind, "label":[newClass[singleAnnoSplitData[0]]], "shape":"rectangle", "points":[firstPoint,secondPoint,thirdPoint,fourthPoint], "notes":"", "annotationGroups":"", "attributes":{}, "imageWidth":undefined, "imageHeight":undefined }) }) /* End */ let x = JSON.stringify(preAnno) this.updateAnnotations(x)
6. Load pre-annotations from an input field (When imported from cloud json files)
let preAnno = inputValues["Preannotations"] if(!isNaN(currentUserLevel) && currentUserLevel == 1 && Array.isArray(annotations) && annotations.length > 0){ let annoData = [] let preAnnoArr = _.get(preAnno, "document.page[0].block", []) let tmAnnoFormat = preAnnoArr.map((val, i) => { return ({ "id":i, "label":["car"], "shape":"rectangle", "points":[firstPoint,secondPoint,thirdPoint,fourthPoint], "notes":"", "annotationGroups":"", "attributes":{}, "imageWidth":undefined, "imageHeight":undefined }) }) let x = JSON.stringify(tmAnnoFormat) this.updateAnnotations(x) }
7. Group label mandatory check for all objects
/* START */ let alertMessage = [] annotations.forEach(anno => { if(!_.get(anno, "annotationGroups") && !_.get(anno, "label", []).includes("other_value_text")) { this.allowSubmit = false alertMessage.push(`One of the objects of class ${_.get(anno, "label", [])[0]} of shape ${_.get(anno, "shape", "")} has not been added to a group.`) } }) if(alertMessage.length){ this.displayErrors(alertMessage) } else { this.allowSubmit = true } /* END */
8. NER: Load pre-annotations
let x = {"text":"Ceci","start":1,"end":5,"id":0,"ws":true} var anns = {} anns["version"] = 2 anns["annotations"] = {} anns["annotations"]["entities"] = [] anns["annotations"]["entities"].push({ "uuid": "1", "start_offset": x["start"], "end": x["end"], "entity_pos":"", "text": x["text"], "attributes":{}, "taskId":"3517133", "entity_type":"ORG" }) let updateAnnoData = [{ "key": "3517133", "value": anns }] console.log('preanns', updateAnnoData) this.updateAnnotations(updateAnnoData)
9. Track QC rejections using custom field.
let currentUserId = localStorage.getItem("userId") if(currentUserLevel === 1){ this.changeFormValue("annotator_id", `${currentUserId}`) } if(currentUserLevel === 2){ this.changeFormValue("QA_id", `${currentUserId}`) if(rejectTaskDetails.commentHistory.length > 0){ console.log("QA_Flag set to true on load") this.changeFormValue("QA_Flag", true) } }
10. Format the NER output to plain text and write to an output field.
/* O/P format: "gemini(brand),100 grams(quantity) */ let entities = _.get(annotations, "[0].value.annotations.entities", _.get(annotations, "annotations.entities", [])) if (entities.length > 0) { entities.sort((a,b) => a.start_offset - b.start_offset) var requiredOp = entities.reduce((accumulator, currentValue) => accumulator + `${currentValue.text}(${currentValue.entity_type}),`, "") if(requiredOp[requiredOp.length - 1] == ","){ requiredOp = requiredOp.slice(0, requiredOp.length-1) } this.changeFormValue("tagged_output", requiredOp)
11. Convert SRT to Taskmonk JSON and load pre-annotation for audio transcription.
function time2sec(dateTime) { let ms = dateTime.split(',')[1] let hh = dateTime.split(':')[0] let mm = dateTime.split(':')[1] let ss = dateTime.split(':')[2].split(',')[0] let ts = (parseInt(hh) * 3600) + (parseInt(mm) * 60) + parseInt(ss) + (parseInt(ms) / 1000) return ts } let finalValues = [] let inputArray = inputValues["TEXT"].split(/\n\s*\n/) let i=0 while(i < inputArray.length) { i = i + 1 if(inputArray[i]) { let startTime = time2sec(inputArray[i].split('\n')[1].split('-->')[0]) let endTime = time2sec(inputArray[i].split('\n')[1].split('-->')[1]) finalValues.push( { "id": i, "curTime": 0, "endTime": endTime, "startTime": startTime , "UIstartTime": startTime, "UIendTime": endTime, "split": 0, "audiourl": "", "pos": 0, "transliterationData": inputArray[i].split('\n')[2].split(" ").map(w => ({ "word": w, "tags": [], "languageTag": "", "editedText": "" })) }) } } let x = JSON.stringify(finalValues) this.updateAnnotations(x)
12. On load custom code to convert gdrive sharing urls to viewing url.
function getIdFromUrl(url) { return url.match(/[-\w]{25,}/); } let inputUrl = getIdFromUrl(inputValues["image"])[0] this.changeInputValue("image", "https://drive.google.com/uc?export=view&id="+inputUrl)
13. Excel view: Update all visible tasks based on changes in one task
function getDupProdIds(tId, selectedInputValues, that) { let dupProdIds = [] let selectedItemId = selectedInputValues["CATLG_ITEM_ID"] for (let i = 0; i < allTasks.length; i++) { let currentTaskInput = that.getInputValueByID(allTasks[i].taskValues.taskId) let currentTaskItemId = currentTaskInput["CATLG_ITEM_ID"] if(currentTaskItemId == selectedItemId) dupProdIds.push(currentTaskInput["productID"]) } return dupProdIds } let that = this let tIdsInputValues = this.getInputValueByID(tID) let val = values["Select Action"]; if(val == "Duplicate"){ let dpValuIds = getDupProdIds(tID, tIdsInputValues, that) //dpValuIds() console.log("dup", dpValuIds) this.changeFormValue("dup_productID", dpValuIds.join(), tID) } else { this.changeFormValue("dup_productID", tIdsInputValues["productID"], tID) }
14. Enable/Disable Buttons:
The following custom code can be plugged in to disable/enable a few actions buttons conditionally.
i. To hide user action buttons
this.hideUserTaskButtons(["button 1 name", "button 2 name", …n])
ii. To unhide user action buttons
this.unhideUserTaskButtons(["button 1 name", "button 2 name", …n])
Available options for button names:
"SUBMIT & GET NEXT TASK" "SAVE & EXIT" "SKIP & GET NEXT TASK" "SUBMIT TASK & EXIT"
15. Enable/Disable fields in Excel View/Bulk tasks.
let intentValue = values["Field_name"] if (intentValue == "Field_name") { this.enableField("Field_name",tID) this.enableField("Field_name",tID) this.mandatoryField("Field_name",true,tID) this.mandatoryField("Field_name",true,tID) } else if (intentValue == "Field_value" || intentValue == "Field_value" || intentValue == "Field_value" || intentValue == "Field_value" || intentValue == "Field_value" ) { this.disableField("Field_name",tID) this.disableField("Field_name",tID) this.changeFormValue("Field_name", " ",tID) this.changeFormValue("Field_name", " ",tID) }
16. Extract ASIN,customID and keywords from Amazon URL’s
let getUrl = values["Competitor Exact URL"] function ExtractASIN(url){ var ASINreg = new RegExp(/(?:\/)([A-Z0-9]{10})(?:$|\/|\?)/) var cMatch = url.match(ASINreg) if(cMatch == null){ return null } return cMatch[1] } function ExtractCID(urls){ var CustomCID = new RegExp(/customId=([A-Za-z0-9]+)/) var cMatch1 = (urls).match(CustomCID) if(cMatch1 == null){ return null } return cMatch1[1] } function ExtractKeywords(url) { const regex = /keywords=([^&]+)/; const match = url.match(regex); const keywords = match ? decodeURIComponent(match[1].replace(/\+/g, ' ')) : null; return keywords.replace(/\+/g, ' ');; } this.changeFormValue("Comp_ASIN", ExtractASIN(getUrl)) if (ExtractCID(getUrl)== null){ this.changeFormValue("CustomID", "Not available") } else{ this.changeFormValue("CustomID", ExtractCID(getUrl)) } if (ExtractKeywords(getUrl)== null){ this.changeFormValue("Source_Of_Search", "Not available") } else { this.changeFormValue("Source_Of_Search", ExtractKeywords(getUrl)) }
17. Generate rows in a name-value pair field
let lineItems = [] let i = 0 /* To generate two empty rows */ while(i < 2) { lineItems.push({ "weight": "", "unit": "" }) i += 1 } /* Variant is the field name */ if(Array.isArray(lineItems)){ this.changeFormValue("Variant", JSON.stringify(lineItems)) }
Calculation of IOU at level 3 for rejection logic
if(currentUserLevel == 3) { let totalIOU = 0 let totalAnnotations = annotations.length for (let step = 0; step < totalAnnotations; step++) { // Current level annotation points for a box let ann1 = annotations[step]["points"] // Get previous level annotation points for the same box let ann2 = JSON.parse(initialValues["Annotations"])[step]["points"] let x1 = ann1[0][0] let x2 = ann1[2][0] let x3 = ann2[0][0] let x4 = ann2[2][0] let y1 = ann1[0][1] let y2 = ann1[2][1] let y3 = ann2[0][1] let y4 = ann2[2][1] let x_inter1 = Math.max(x1, x3) let x_inter2 = Math.min(x2, x4) let y_inter1 = Math.max(y1, y3) let y_inter2 = Math.min(y2, y4) console.log(x_inter1, y_inter2) let width = x_inter2 -x_inter1 let height = y_inter2 - y_inter1 let area_inter = width * height let width_box1 = x2 - x1 let width_box2 = x4 - x3 let height_box1 = y2 - y1 let height_box2 = y4 - y3 let area_box1 = width_box1 * height_box1 let area_box2 = width_box2 * height_box2 let area_union = area_box1 + area_box2 - area_inter let iou = area_inter / area_union totalIOU = totalIOU + iou } //Average IOU between level 3 and previous level annotations let avgIOU = totalIOU/totalAnnotations if (avgIOU < 0.90) { alert("IOU dropped below 0.9") } }