Module tf.browser.ner.fragments
Wraps various pieces into HTML.
This module generates HTML for various controls that appear in the TF browser.
Classes
class Fragments
-
Expand source code Browse git
class Fragments: def wrapMessages(self): """HTML for messages.""" v = self.v messageSrc = v.messagesrc v.messages = H.p((H.span(text, cls=lev) + H.br() for (lev, text) in messageSrc)) def wrapSets(self): """HTML for the annotation task chooser. It is a dropdown with options, each corresponding to an existing annotation task. There is also a control to delete the task. Apart from these buttons there is a button to switch to the entities that are present in the TF dataset as nodes of the entity type specified in the YAML file with corresponding features. Finally, it is possible to create a new, empty annotation task. """ ner = self.ner taskNames = ner.getTasks() v = self.v chosenTask = v.task sheetCase = v.sheetcase content1 = [ H.input( type="hidden", name="task", value=chosenTask, id="seth", ), H.input( type="hidden", name="duset", value="", id="duseth", ), H.input( type="hidden", name="rset", value="", id="rseth", ), H.button( "+", type="submit", id="anew", title="create a new annotation task", cls="mono", ), " ", H.button( "++", type="submit", id="adup", title="duplicate this annotation task", cls="mono", ), " ", H.select( ( H.option( ner.setInfo(taskName)[0], value=taskName, selected=taskName == chosenTask, ) for taskName in [""] + sorted(taskNames) ), cls="selinp", id="achange", ), " ", H.input( type="hidden", id="sheetcase", name="sheetcase", value="v" if sheetCase else "x", ), H.button( "C" if sheetCase else "¢", type="submit", id="sheetcasebutton", title="toggle case-sensitivity", cls="mono", ), ] content2 = ( [ H.input( type="hidden", name="dset", value="", id="dseth", ), H.button( "→", type="submit", id="arename", title="rename current annotation task", cls="mono", ), " ", H.button( "-", type="submit", id="adelete", title="delete current annotation task", cls="mono", ), ] if not (chosenTask == "" or chosenTask.startswith(".")) else [] ) v.tasks = H.p(content1, content2) def wrapCaption(self): """HTML for the caption of the entity list.""" v = self.v ner = self.ner setNameRep = ner.setNameRep setIsSrc = ner.setIsSrc setIsRo = ner.setIsRo v.caption = H.p(H.b(setNameRep)) + H.p( "Entities as " + ( "given in the source" if setIsSrc else "specified by a spreadsheet" if setIsRo else "marked up by hand" ) ) def wrapLogs(self): """HTML for the log messages produced when processing a sheet.""" v = self.v ner = self.ner sheetData = ner.getSheetData() logData = sheetData.logData or [] v.logs = "\n".join( H.p( H.nb * indent + msg, cls="msg " + ("special" if isError is None else "error" if isError else "info"), ) for (isError, indent, msg) in logData ) def wrapQuery(self): """HTML for all control widgets on the page.""" self.wrapAppearance() self.wrapFilter() self.wrapEntity() self.wrapEntityText() self.wrapScope() self.wrapEntityFeats() self.wrapEntityModReport() self.wrapEntityModify() def wrapAppearance(self): """HTML for the appearance widget. The appearance widget lets the user choose how inline entities should appear: with or without underlining, identifier, kind, frequency. """ v = self.v ner = self.ner settings = ner.settings features = settings.features formattingDo = v.formattingdo formattingState = v.formattingstate v.formattingbuttons = H.join( H.span( H.input( type="hidden", name="formattingdo", value="v" if formattingDo else "x", ), H.button( "decorated" if formattingDo else "plain", type="button", main="v", title="toggle plain or decorated formatting of entities", cls="mono", ), ), H.span( [ H.span( H.input( type="hidden", name=f"{feat}_appearance", value="v" if formattingState[feat] else "x", ), H.button( ( "stats" if feat == "_stat_" else "underlining" if feat == "_entity_" else feat ), feat=feat, type="button", title=( "toggle display of statistics" if feat == "_stat_" else ( "toggle formatting of entities" if feat == "_entity" else f"toggle formatting for feature {feat}" ) ), cls="active" if formattingState[feat] else "", ), ) for feat in features + ("_stat_", "_entity_") ], id="decoratewidget", ), sep=" ", ) def wrapFilter(self): """HTML for the filter widget. The filter widget lets the user filter the buckets by a search pattern or the condition that the buckets contains entities (and the even more useful condition that the buckets do *not* contain entities). """ v = self.v ner = self.ner setData = ner.getSetData() bFind = v.bfind bFindC = v.bfindc bFindRe = v.bfindre bFindError = v.bfinderror anyEnt = v.anyent v.hasfilter = bFindRe is not None or anyEnt is not None v.nbuckets = len(setData.buckets or []) v.filterwidget = H.div( ( H.b("Filter"), H.join( H.span( H.input(type="text", name="bfind", id="bfind", value=bFind), H.input( type="hidden", name="bfindc", id="bfindc", value="v" if bFindC else "x", ), H.button( "C" if bFindC else "¢", type="submit", id="bfindb", title=( "using case SENSITIVE search" if bFindC else "using case INSENSITIVE search" ), cls="mono", ), " ", H.button("❌", type="submit", id="findclear", cls="icon"), " ", H.span(bFindError, id="bfinderror", cls="error"), cls="filtercomponent", ), H.span( H.input( type="hidden", name="anyent", id="anyent", value="" if anyEnt is None else "v" if anyEnt else "x", ), H.button( ( "with or without" if anyEnt is None else "with" if anyEnt else "without" ), type="submit", id="anyentbutton", cls="mono", ), H.span(" marked entities"), cls="filtercomponent", ), H.span( H.button("🔎", type="submit", id="lookupf", cls="alt"), cls="filtercomponent", ), H.span( self.wrapFindStat(), cls="filtercomponent", ), ), ), id="filterwidget", ) def wrapEntity(self): """Basic data for the selected entity widget. The entity widget shows the occurrence or entity that is selected. This function computed the relevant values and stores them in hidden input elements. """ v = self.v ner = self.ner settings = ner.settings features = settings.features activeEntity = v.activeentity activeTrigger = v.activetrigger tokenStart = v.tokenstart tokenEnd = v.tokenend hasEnt = activeEntity is not None hasOcc = tokenStart is not None and tokenEnd is not None if hasEnt: v.tokenstart = None v.tokenend = None txt = "" eTxt = repIdent(features, activeEntity, active="active") elif hasOcc: v.activeentity = None v.activetrigger = None txt = ( ner.textFromSlots(range(tokenStart, tokenEnd + 1)) if tokenStart and tokenEnd else "" ) eTxt = "" else: v.activeentity = None v.activetrigger = None v.tokenstart = None v.tokenend = None txt = "" eTxt = "" v.activeentityrep = "⊙".join(activeEntity) if activeEntity else "" v.activetriggerrep = "⊙".join(activeTrigger) if activeTrigger else "" tokenStart = v.tokenstart tokenEnd = v.tokenend startRep = H.input( type="hidden", name="tokenstart", id="tokenstart", value=tokenStart or "" ) endRep = H.input( type="hidden", name="tokenend", id="tokenend", value=tokenEnd or "" ) v.entityinit = startRep + endRep v.txt = txt v.etxt = eTxt def wrapEntityHeaders(self): """HTML for the header of the entity table, dependent on the state of sorting.""" v = self.v sortKey = v.sortkey sortDir = v.sortdir if not sortKey and not sortDir: (sortKey, sortDir) = SORT_DEFAULT else: if not sortKey: sortKey = SORTKEY_DEFAULT if not sortDir: sortDir = SORTDIR_DEFAULT ner = self.ner settings = ner.settings features = settings.features setIsX = ner.setIsX sortKeys = ( (("name", "sort_0"),) if setIsX else ((feat, f"sort_{i}") for (i, feat) in enumerate(features)) ) content = [ H.input(type="hidden", name="sortkey", id="sortkey", value=sortKey), H.input(type="hidden", name="sortdir", id="sortdir", value=sortDir), ] for label, key in (("frequency", "freqsort"), *sortKeys): hl = " active " if key == sortKey else "" theDir = sortDir if key == sortKey else SORTDIR_ASC theArrow = "↑" if theDir == SORTDIR_ASC else "↓" content.extend( [ H.button( f"{label} {theArrow}", type="button", tp="sort", sk=key, sd=theDir, cls=f"alt{hl}", ), " ", ] ) content.append(self.wrapSubtle()) return H.p(content) def wrapSubtle(self): """HTML for the button to filter the entity list by scoped/nonscoped triggers.""" v = self.v subtleFilter = v.subtlefilter ner = self.ner setIsX = ner.setIsX return H.span( H.input( type="hidden", name="subtlefilter", id="subtlefilter", value="" if subtleFilter is None else "v" if subtleFilter else "x", ), ( "" if not setIsX else H.button( ( "all scopes" if subtleFilter is None else "scoped" if subtleFilter else "unscoped" ), type="submit", id="subtlefilterbutton", cls="mono", ) ), cls="subtlefilter", ) def wrapEntityText(self): """HTML for the selected entity widget.""" v = self.v ner = self.ner setIsX = ner.setIsX freeState = v.freestate txt = v.txt eTxt = v.etxt title = "choose: free, intersecting with other entities, or all" v.entitytext = ( "" if setIsX else H.div( ( H.b("Mark"), H.span( H.join( H.span(txt if txt else eTxt or "", id="qtextentshow"), " ", H.button("❌", type="submit", id="queryclear", cls="icon"), " ", H.button( "✅", type="submit", id="lookupq", cls="icon", title="look up and fill in green fields", ), H.button( "❎", type="submit", id="lookupn", cls="icon", title="look up and keep green fields as is", ), H.input( type="hidden", name="freestate", id="freestate", value=freeState, ), H.button( ( "⚭ intersecting" if freeState == "bound" else "⚯ free" if freeState == "free" else "⚬ all" ), type="submit", id="freebutton", cls="mono", title=title, ), ), id="etextwidget", ), ), id="markwidget", ) ) def wrapEntityFeats(self): """HTML for the entity feature value selection. All feature values of entities that occupy the selected occurrences are shown, with the possibility that the user selects some of these values, thereby selecting a subset of the original set of occurrences. """ v = self.v ner = self.ner setIsX = ner.setIsX bucketType = ner.bucketType settings = ner.settings features = settings.features setData = ner.getSetData() txt = v.txt eTxt = v.etxt valSelect = v.valselect scopeInit = v.scopeinit scopeFilter = v.scopefilter hasOcc = txt != "" hasEnt = eTxt != "" featuresW = { feat: ( valSelect[feat] if hasEnt else setData.entityTextVal[feat].get(txt, set()) ) for feat in features } content = [] inputContent = [] for feat, theseVals in featuresW.items(): thisValSelect = valSelect[feat] valuesContent = [] inputContent.append( H.input( type="hidden", name=f"{feat}_select", id=f"{feat}_select", value=",".join(thisValSelect), ) ) if hasEnt or hasOcc: for val in [NONE] + sorted(theseVals): valuesContent.append( H.button( val, self.wrapEntityStat(val, feat), type="button", name=val or EMPTY, cls=f"{feat}_sel alt", st="v" if val in thisValSelect else "x", title=( f"{feat} not marked" if val == NONE else f"{feat} marked as {val}" ), ) ) titleContent = H.div(H.i(f"{feat}:"), cls="feattitle") else: titleContent = "" content.append(H.div(titleContent, valuesContent, cls="featwidget")) total = self.wrapEntityStat(None, "") v.selectentities = ( H.div( H.join(inputContent), ( H.div( H.span("" if setIsX else H.b("Select"), scopeInit, scopeFilter), H.span(H.span(f"{total} {bucketType}(s)")), ) if hasEnt else H.join( H.div( H.p(H.b("Select"), scopeInit, scopeFilter), H.p(H.span(f"{total} {bucketType}(s)")), ), H.div(content, id="selectsubwidget"), ) ), id="selectwidget", ) if hasEnt or hasOcc else H.join(inputContent) ) def wrapScope(self): """HTML for the scope widget. The scope widget lets the user choose whether the add / del actions should be applied to all relevant buckets, or only to the filtered buckets. """ v = self.v ner = self.ner setIsRo = ner.setIsRo scope = v.scope hasFilter = v.hasfilter txt = v.txt eTxt = v.etxt hasOcc = txt != "" hasEnt = eTxt != "" scopeInit = H.input(type="hidden", id="scope", name="scope", value=scope) scopeFilter = "" if (not setIsRo) and (hasOcc or hasEnt): # Scope of modification scopeFilter = ( H.span( H.button("", type="button", id="scopebutton", title="", cls="alt") ) if hasFilter else "" ) v.scopeinit = scopeInit v.scopefilter = scopeFilter def wrapExceptions(self): """HTML for the select / deselect buttons. These buttons appear at the end of selected occurrences in the text displayed in the buckets. The user can select or deselect individual entities for the application of the add / del operations. """ v = self.v txt = v.txt eTxt = v.etxt ner = self.ner bucketType = ner.bucketType setIsRo = ner.setIsRo hasOcc = txt != "" hasEnt = eTxt != "" scopeExceptions = "" if (not setIsRo) and (hasOcc or hasEnt): scopeExceptions = H.span( H.nb, H.button( "✅", type="button", id="selectall", title=f"select all occurrences in filtered {bucketType}s", cls="icon", ), " ", H.button( "❌", type="button", id="selectnone", title=f"deselect all occurrences in filtered {bucketType}s", cls="icon", ), ) return scopeExceptions def wrapEntityModify(self): """HTML for the add / del widget. This widget contains controls to specify which entity feature values should be added or deleted. Considerable effort is made to prefill these components with ergonomic values. """ v = self.v ner = self.ner settings = ner.settings features = settings.features keywordFeatures = settings.keywordFeatures featureDefault = ner.featureDefault setData = ner.getSetData() setIsRo = ner.setIsRo txt = v.txt eTxt = v.etxt submitter = v.submitter activeEntity = v.activeentity tokenStart = v.tokenstart tokenEnd = v.tokenend delData = v.adddata addData = v.adddata modWidgetState = v.modwidgetstate excludedTokens = v.excludedtokens deletions = delData.deletions additions = addData.additions freeVals = addData.freeVals hasOcc = txt != "" hasEnt = eTxt != "" delButtonHtml = "" addButtonHtml = "" delContentHtml = [] addContentHtml = [] # Assigment of feature values somethingToDelete = True if (not setIsRo) and (hasOcc or hasEnt): instances = self.wrapExceptions() for i, feat in enumerate(features): isKeyword = feat in keywordFeatures theseVals = ( {activeEntity[i]} if hasEnt else sorted(setData.entityTextVal[feat].get(txt, set())) ) allVals = ( sorted(x[0] for x in setData.entityFreq[feat]) if isKeyword else theseVals ) addVals = ( additions[i] if additions is not None and len(additions) > i else set() ) delVals = ( deletions[i] if deletions is not None and len(deletions) > i else set() ) freeVal = ( freeVals[i] if freeVals is not None and len(freeVals) > i else None ) default = ( activeEntity[i] if hasEnt else ( featureDefault[feat](range(tokenStart, tokenEnd + 1)) if hasOcc else {} ) ) titleContent = H.div( H.i(f"{feat}:"), cls="feattitle", ) delValuesContent = [] addValuesContent = [] hasSomeVals = False for val in allVals: occurs = val in theseVals delSt = "minus" if hasEnt or val in delVals else "x" addSt = ( "plus" if val in addVals else "plus" if val == default and freeVal != default else "x" ) if occurs: delValuesContent.append( H.div( [ H.span( val or H.nb, cls=f"{feat}_sel", st=delSt, val=val, ), ], cls=f"{feat}_w modval", ) ) hasSomeVals = True addValuesContent.append( H.div( [ H.span( val or H.nb, cls=f"{feat}_sel", st=addSt, val=val ), ], cls=f"{feat}_w modval", ) ) if not hasSomeVals: somethingToDelete = False init = "" if default in theseVals else default val = ( addVals[0] if len(addVals) and submitter in {"lookupn", "freebutton"} else ( init if submitter == "lookupq" else freeVal if freeVal is not None else init ) ) addSt = ( "plus" if val and len(addVals) and submitter in {"lookupn", "freebutton"} else ( "plus" if submitter == "lookupq" and val else ( "plus" if val == freeVal else "plus" if init and len(theseVals) == 0 else "x" ) ) ) if (isKeyword and val in allVals) or val is None: val = "" addSt = "x" addValuesContent.append( H.div( [H.input(type="text", st=addSt, value=val, origval=val)], cls="modval", ) ) delContentHtml.append( H.div( titleContent, H.div(delValuesContent, cls="modifyvalues"), cls="delfeat", feat=feat, ) ) addContentHtml.append( H.div( titleContent, H.div(addValuesContent, cls="modifyvalues"), cls="addfeat", feat=feat, ) ) delButtonHtml = H.span( H.button("Delete", type="button", id="delgo", value="v", cls="special"), H.input(type="hidden", id="deldata", name="deldata", value=""), ) addButtonHtml = H.span( H.button("Add", type="button", id="addgo", value="v", cls="special"), H.input(type="hidden", id="adddata", name="adddata", value=""), ) delResetHtml = H.button( "⌫", type="button", id="delresetbutton", title="clear values in form", cls="icon", ) addResetHtml = H.button( "⌫", type="button", id="addresetbutton", title="clear values in form", cls="icon", ) delWidgetContent = ( H.div( H.span( H.span(delButtonHtml, delResetHtml, id="modifyhead"), H.span(delContentHtml, cls="assignwidget"), ), H.span("", id="delfeedback", cls="feedback"), id="delwidget", ) if somethingToDelete else "" ) v.modifyentity = H.div( H.input( type="hidden", id="modwidgetstate", name="modwidgetstate", value=modWidgetState, ), H.input( type="hidden", id="excludedtokens", name="excludedtokens", value=",".join(str(t) for t in excludedTokens), ), H.b("Modify"), instances, delWidgetContent, H.div( H.span( H.span(addButtonHtml, addResetHtml, id="modifyhead"), H.span(addContentHtml, cls="assignwidget"), ), H.span("", id="addfeedback", cls="feedback"), id="addwidget", ), id="modwidget", ) def wrapFindStat(self): """HTML for statistics. This is about totals of occurrences in all buckets versus in filtered buckets. """ v = self.v nBuckets = v.nbuckets nFind = v.nfind hasFilter = v.hasfilter n = f"{nFind} of {nBuckets}" if hasFilter else nBuckets return H.span(n, cls="stat") def wrapEntityStat(self, val, feat): """HTML for statistics of feature values. This is about totals of occurrences of feature values in all buckets versus in filtered buckets. """ v = self.v nVisible = v.nvisible nEnt = v.nent hasFilter = v.hasfilter thisNVisible = nVisible[feat] thisNEnt = nEnt[feat] na = thisNEnt[val] n = ( (H.span(f"{thisNVisible[val]} of ", cls="filted") + f"{na}") if hasFilter else f"{na}" ) return H.span(n, cls="stat") def wrapActive(self): """HTML for the active entity.""" v = self.v activeVal = v.activeval v.activevalrep = H.join( H.input( type="hidden", id=f"{feat}_active", name=f"{feat}_active", value=val or "", ) for (feat, val) in activeVal.items() ) def wrapEntityModReport(self): """HTML for the combined report of add / del actions.""" v = self.v reportDel = v.reportdel reportAdd = v.reportadd v.modifyreport = H.join( H.div(reportDel, id="delreport", cls="report"), H.div(reportAdd, id="addreport", cls="report"), ) def wrapReport(self, report, kind): """HTML for the report of add / del actions.""" v = self.v ner = self.ner settings = ner.settings features = settings.features label = "Deletion" if kind == "del" else "Addition" if kind == "add" else "" v[f"report{kind}"] = H.join( H.div( ( H.join( H.div(f"{label}: {n} x {valRep(features, fVals)}") for (fVals, n) in line ) if type(line) is tuple else line ), cls="report", ) for line in report ) report.clear()
Subclasses
Methods
def wrapActive(self)
-
Expand source code Browse git
def wrapActive(self): """HTML for the active entity.""" v = self.v activeVal = v.activeval v.activevalrep = H.join( H.input( type="hidden", id=f"{feat}_active", name=f"{feat}_active", value=val or "", ) for (feat, val) in activeVal.items() )
HTML for the active entity.
def wrapAppearance(self)
-
Expand source code Browse git
def wrapAppearance(self): """HTML for the appearance widget. The appearance widget lets the user choose how inline entities should appear: with or without underlining, identifier, kind, frequency. """ v = self.v ner = self.ner settings = ner.settings features = settings.features formattingDo = v.formattingdo formattingState = v.formattingstate v.formattingbuttons = H.join( H.span( H.input( type="hidden", name="formattingdo", value="v" if formattingDo else "x", ), H.button( "decorated" if formattingDo else "plain", type="button", main="v", title="toggle plain or decorated formatting of entities", cls="mono", ), ), H.span( [ H.span( H.input( type="hidden", name=f"{feat}_appearance", value="v" if formattingState[feat] else "x", ), H.button( ( "stats" if feat == "_stat_" else "underlining" if feat == "_entity_" else feat ), feat=feat, type="button", title=( "toggle display of statistics" if feat == "_stat_" else ( "toggle formatting of entities" if feat == "_entity" else f"toggle formatting for feature {feat}" ) ), cls="active" if formattingState[feat] else "", ), ) for feat in features + ("_stat_", "_entity_") ], id="decoratewidget", ), sep=" ", )
HTML for the appearance widget.
The appearance widget lets the user choose how inline entities should appear: with or without underlining, identifier, kind, frequency.
def wrapCaption(self)
-
Expand source code Browse git
def wrapCaption(self): """HTML for the caption of the entity list.""" v = self.v ner = self.ner setNameRep = ner.setNameRep setIsSrc = ner.setIsSrc setIsRo = ner.setIsRo v.caption = H.p(H.b(setNameRep)) + H.p( "Entities as " + ( "given in the source" if setIsSrc else "specified by a spreadsheet" if setIsRo else "marked up by hand" ) )
HTML for the caption of the entity list.
def wrapEntity(self)
-
Expand source code Browse git
def wrapEntity(self): """Basic data for the selected entity widget. The entity widget shows the occurrence or entity that is selected. This function computed the relevant values and stores them in hidden input elements. """ v = self.v ner = self.ner settings = ner.settings features = settings.features activeEntity = v.activeentity activeTrigger = v.activetrigger tokenStart = v.tokenstart tokenEnd = v.tokenend hasEnt = activeEntity is not None hasOcc = tokenStart is not None and tokenEnd is not None if hasEnt: v.tokenstart = None v.tokenend = None txt = "" eTxt = repIdent(features, activeEntity, active="active") elif hasOcc: v.activeentity = None v.activetrigger = None txt = ( ner.textFromSlots(range(tokenStart, tokenEnd + 1)) if tokenStart and tokenEnd else "" ) eTxt = "" else: v.activeentity = None v.activetrigger = None v.tokenstart = None v.tokenend = None txt = "" eTxt = "" v.activeentityrep = "⊙".join(activeEntity) if activeEntity else "" v.activetriggerrep = "⊙".join(activeTrigger) if activeTrigger else "" tokenStart = v.tokenstart tokenEnd = v.tokenend startRep = H.input( type="hidden", name="tokenstart", id="tokenstart", value=tokenStart or "" ) endRep = H.input( type="hidden", name="tokenend", id="tokenend", value=tokenEnd or "" ) v.entityinit = startRep + endRep v.txt = txt v.etxt = eTxt
Basic data for the selected entity widget.
The entity widget shows the occurrence or entity that is selected. This function computed the relevant values and stores them in hidden input elements.
def wrapEntityFeats(self)
-
Expand source code Browse git
def wrapEntityFeats(self): """HTML for the entity feature value selection. All feature values of entities that occupy the selected occurrences are shown, with the possibility that the user selects some of these values, thereby selecting a subset of the original set of occurrences. """ v = self.v ner = self.ner setIsX = ner.setIsX bucketType = ner.bucketType settings = ner.settings features = settings.features setData = ner.getSetData() txt = v.txt eTxt = v.etxt valSelect = v.valselect scopeInit = v.scopeinit scopeFilter = v.scopefilter hasOcc = txt != "" hasEnt = eTxt != "" featuresW = { feat: ( valSelect[feat] if hasEnt else setData.entityTextVal[feat].get(txt, set()) ) for feat in features } content = [] inputContent = [] for feat, theseVals in featuresW.items(): thisValSelect = valSelect[feat] valuesContent = [] inputContent.append( H.input( type="hidden", name=f"{feat}_select", id=f"{feat}_select", value=",".join(thisValSelect), ) ) if hasEnt or hasOcc: for val in [NONE] + sorted(theseVals): valuesContent.append( H.button( val, self.wrapEntityStat(val, feat), type="button", name=val or EMPTY, cls=f"{feat}_sel alt", st="v" if val in thisValSelect else "x", title=( f"{feat} not marked" if val == NONE else f"{feat} marked as {val}" ), ) ) titleContent = H.div(H.i(f"{feat}:"), cls="feattitle") else: titleContent = "" content.append(H.div(titleContent, valuesContent, cls="featwidget")) total = self.wrapEntityStat(None, "") v.selectentities = ( H.div( H.join(inputContent), ( H.div( H.span("" if setIsX else H.b("Select"), scopeInit, scopeFilter), H.span(H.span(f"{total} {bucketType}(s)")), ) if hasEnt else H.join( H.div( H.p(H.b("Select"), scopeInit, scopeFilter), H.p(H.span(f"{total} {bucketType}(s)")), ), H.div(content, id="selectsubwidget"), ) ), id="selectwidget", ) if hasEnt or hasOcc else H.join(inputContent) )
HTML for the entity feature value selection.
All feature values of entities that occupy the selected occurrences are shown, with the possibility that the user selects some of these values, thereby selecting a subset of the original set of occurrences.
def wrapEntityHeaders(self)
-
Expand source code Browse git
def wrapEntityHeaders(self): """HTML for the header of the entity table, dependent on the state of sorting.""" v = self.v sortKey = v.sortkey sortDir = v.sortdir if not sortKey and not sortDir: (sortKey, sortDir) = SORT_DEFAULT else: if not sortKey: sortKey = SORTKEY_DEFAULT if not sortDir: sortDir = SORTDIR_DEFAULT ner = self.ner settings = ner.settings features = settings.features setIsX = ner.setIsX sortKeys = ( (("name", "sort_0"),) if setIsX else ((feat, f"sort_{i}") for (i, feat) in enumerate(features)) ) content = [ H.input(type="hidden", name="sortkey", id="sortkey", value=sortKey), H.input(type="hidden", name="sortdir", id="sortdir", value=sortDir), ] for label, key in (("frequency", "freqsort"), *sortKeys): hl = " active " if key == sortKey else "" theDir = sortDir if key == sortKey else SORTDIR_ASC theArrow = "↑" if theDir == SORTDIR_ASC else "↓" content.extend( [ H.button( f"{label} {theArrow}", type="button", tp="sort", sk=key, sd=theDir, cls=f"alt{hl}", ), " ", ] ) content.append(self.wrapSubtle()) return H.p(content)
HTML for the header of the entity table, dependent on the state of sorting.
def wrapEntityModReport(self)
-
Expand source code Browse git
def wrapEntityModReport(self): """HTML for the combined report of add / del actions.""" v = self.v reportDel = v.reportdel reportAdd = v.reportadd v.modifyreport = H.join( H.div(reportDel, id="delreport", cls="report"), H.div(reportAdd, id="addreport", cls="report"), )
HTML for the combined report of add / del actions.
def wrapEntityModify(self)
-
Expand source code Browse git
def wrapEntityModify(self): """HTML for the add / del widget. This widget contains controls to specify which entity feature values should be added or deleted. Considerable effort is made to prefill these components with ergonomic values. """ v = self.v ner = self.ner settings = ner.settings features = settings.features keywordFeatures = settings.keywordFeatures featureDefault = ner.featureDefault setData = ner.getSetData() setIsRo = ner.setIsRo txt = v.txt eTxt = v.etxt submitter = v.submitter activeEntity = v.activeentity tokenStart = v.tokenstart tokenEnd = v.tokenend delData = v.adddata addData = v.adddata modWidgetState = v.modwidgetstate excludedTokens = v.excludedtokens deletions = delData.deletions additions = addData.additions freeVals = addData.freeVals hasOcc = txt != "" hasEnt = eTxt != "" delButtonHtml = "" addButtonHtml = "" delContentHtml = [] addContentHtml = [] # Assigment of feature values somethingToDelete = True if (not setIsRo) and (hasOcc or hasEnt): instances = self.wrapExceptions() for i, feat in enumerate(features): isKeyword = feat in keywordFeatures theseVals = ( {activeEntity[i]} if hasEnt else sorted(setData.entityTextVal[feat].get(txt, set())) ) allVals = ( sorted(x[0] for x in setData.entityFreq[feat]) if isKeyword else theseVals ) addVals = ( additions[i] if additions is not None and len(additions) > i else set() ) delVals = ( deletions[i] if deletions is not None and len(deletions) > i else set() ) freeVal = ( freeVals[i] if freeVals is not None and len(freeVals) > i else None ) default = ( activeEntity[i] if hasEnt else ( featureDefault[feat](range(tokenStart, tokenEnd + 1)) if hasOcc else {} ) ) titleContent = H.div( H.i(f"{feat}:"), cls="feattitle", ) delValuesContent = [] addValuesContent = [] hasSomeVals = False for val in allVals: occurs = val in theseVals delSt = "minus" if hasEnt or val in delVals else "x" addSt = ( "plus" if val in addVals else "plus" if val == default and freeVal != default else "x" ) if occurs: delValuesContent.append( H.div( [ H.span( val or H.nb, cls=f"{feat}_sel", st=delSt, val=val, ), ], cls=f"{feat}_w modval", ) ) hasSomeVals = True addValuesContent.append( H.div( [ H.span( val or H.nb, cls=f"{feat}_sel", st=addSt, val=val ), ], cls=f"{feat}_w modval", ) ) if not hasSomeVals: somethingToDelete = False init = "" if default in theseVals else default val = ( addVals[0] if len(addVals) and submitter in {"lookupn", "freebutton"} else ( init if submitter == "lookupq" else freeVal if freeVal is not None else init ) ) addSt = ( "plus" if val and len(addVals) and submitter in {"lookupn", "freebutton"} else ( "plus" if submitter == "lookupq" and val else ( "plus" if val == freeVal else "plus" if init and len(theseVals) == 0 else "x" ) ) ) if (isKeyword and val in allVals) or val is None: val = "" addSt = "x" addValuesContent.append( H.div( [H.input(type="text", st=addSt, value=val, origval=val)], cls="modval", ) ) delContentHtml.append( H.div( titleContent, H.div(delValuesContent, cls="modifyvalues"), cls="delfeat", feat=feat, ) ) addContentHtml.append( H.div( titleContent, H.div(addValuesContent, cls="modifyvalues"), cls="addfeat", feat=feat, ) ) delButtonHtml = H.span( H.button("Delete", type="button", id="delgo", value="v", cls="special"), H.input(type="hidden", id="deldata", name="deldata", value=""), ) addButtonHtml = H.span( H.button("Add", type="button", id="addgo", value="v", cls="special"), H.input(type="hidden", id="adddata", name="adddata", value=""), ) delResetHtml = H.button( "⌫", type="button", id="delresetbutton", title="clear values in form", cls="icon", ) addResetHtml = H.button( "⌫", type="button", id="addresetbutton", title="clear values in form", cls="icon", ) delWidgetContent = ( H.div( H.span( H.span(delButtonHtml, delResetHtml, id="modifyhead"), H.span(delContentHtml, cls="assignwidget"), ), H.span("", id="delfeedback", cls="feedback"), id="delwidget", ) if somethingToDelete else "" ) v.modifyentity = H.div( H.input( type="hidden", id="modwidgetstate", name="modwidgetstate", value=modWidgetState, ), H.input( type="hidden", id="excludedtokens", name="excludedtokens", value=",".join(str(t) for t in excludedTokens), ), H.b("Modify"), instances, delWidgetContent, H.div( H.span( H.span(addButtonHtml, addResetHtml, id="modifyhead"), H.span(addContentHtml, cls="assignwidget"), ), H.span("", id="addfeedback", cls="feedback"), id="addwidget", ), id="modwidget", )
HTML for the add / del widget.
This widget contains controls to specify which entity feature values should be added or deleted.
Considerable effort is made to prefill these components with ergonomic values.
def wrapEntityStat(self, val, feat)
-
Expand source code Browse git
def wrapEntityStat(self, val, feat): """HTML for statistics of feature values. This is about totals of occurrences of feature values in all buckets versus in filtered buckets. """ v = self.v nVisible = v.nvisible nEnt = v.nent hasFilter = v.hasfilter thisNVisible = nVisible[feat] thisNEnt = nEnt[feat] na = thisNEnt[val] n = ( (H.span(f"{thisNVisible[val]} of ", cls="filted") + f"{na}") if hasFilter else f"{na}" ) return H.span(n, cls="stat")
HTML for statistics of feature values.
This is about totals of occurrences of feature values in all buckets versus in filtered buckets.
def wrapEntityText(self)
-
Expand source code Browse git
def wrapEntityText(self): """HTML for the selected entity widget.""" v = self.v ner = self.ner setIsX = ner.setIsX freeState = v.freestate txt = v.txt eTxt = v.etxt title = "choose: free, intersecting with other entities, or all" v.entitytext = ( "" if setIsX else H.div( ( H.b("Mark"), H.span( H.join( H.span(txt if txt else eTxt or "", id="qtextentshow"), " ", H.button("❌", type="submit", id="queryclear", cls="icon"), " ", H.button( "✅", type="submit", id="lookupq", cls="icon", title="look up and fill in green fields", ), H.button( "❎", type="submit", id="lookupn", cls="icon", title="look up and keep green fields as is", ), H.input( type="hidden", name="freestate", id="freestate", value=freeState, ), H.button( ( "⚭ intersecting" if freeState == "bound" else "⚯ free" if freeState == "free" else "⚬ all" ), type="submit", id="freebutton", cls="mono", title=title, ), ), id="etextwidget", ), ), id="markwidget", ) )
HTML for the selected entity widget.
def wrapExceptions(self)
-
Expand source code Browse git
def wrapExceptions(self): """HTML for the select / deselect buttons. These buttons appear at the end of selected occurrences in the text displayed in the buckets. The user can select or deselect individual entities for the application of the add / del operations. """ v = self.v txt = v.txt eTxt = v.etxt ner = self.ner bucketType = ner.bucketType setIsRo = ner.setIsRo hasOcc = txt != "" hasEnt = eTxt != "" scopeExceptions = "" if (not setIsRo) and (hasOcc or hasEnt): scopeExceptions = H.span( H.nb, H.button( "✅", type="button", id="selectall", title=f"select all occurrences in filtered {bucketType}s", cls="icon", ), " ", H.button( "❌", type="button", id="selectnone", title=f"deselect all occurrences in filtered {bucketType}s", cls="icon", ), ) return scopeExceptions
HTML for the select / deselect buttons.
These buttons appear at the end of selected occurrences in the text displayed in the buckets. The user can select or deselect individual entities for the application of the add / del operations.
def wrapFilter(self)
-
Expand source code Browse git
def wrapFilter(self): """HTML for the filter widget. The filter widget lets the user filter the buckets by a search pattern or the condition that the buckets contains entities (and the even more useful condition that the buckets do *not* contain entities). """ v = self.v ner = self.ner setData = ner.getSetData() bFind = v.bfind bFindC = v.bfindc bFindRe = v.bfindre bFindError = v.bfinderror anyEnt = v.anyent v.hasfilter = bFindRe is not None or anyEnt is not None v.nbuckets = len(setData.buckets or []) v.filterwidget = H.div( ( H.b("Filter"), H.join( H.span( H.input(type="text", name="bfind", id="bfind", value=bFind), H.input( type="hidden", name="bfindc", id="bfindc", value="v" if bFindC else "x", ), H.button( "C" if bFindC else "¢", type="submit", id="bfindb", title=( "using case SENSITIVE search" if bFindC else "using case INSENSITIVE search" ), cls="mono", ), " ", H.button("❌", type="submit", id="findclear", cls="icon"), " ", H.span(bFindError, id="bfinderror", cls="error"), cls="filtercomponent", ), H.span( H.input( type="hidden", name="anyent", id="anyent", value="" if anyEnt is None else "v" if anyEnt else "x", ), H.button( ( "with or without" if anyEnt is None else "with" if anyEnt else "without" ), type="submit", id="anyentbutton", cls="mono", ), H.span(" marked entities"), cls="filtercomponent", ), H.span( H.button("🔎", type="submit", id="lookupf", cls="alt"), cls="filtercomponent", ), H.span( self.wrapFindStat(), cls="filtercomponent", ), ), ), id="filterwidget", )
HTML for the filter widget.
The filter widget lets the user filter the buckets by a search pattern or the condition that the buckets contains entities (and the even more useful condition that the buckets do not contain entities).
def wrapFindStat(self)
-
Expand source code Browse git
def wrapFindStat(self): """HTML for statistics. This is about totals of occurrences in all buckets versus in filtered buckets. """ v = self.v nBuckets = v.nbuckets nFind = v.nfind hasFilter = v.hasfilter n = f"{nFind} of {nBuckets}" if hasFilter else nBuckets return H.span(n, cls="stat")
HTML for statistics.
This is about totals of occurrences in all buckets versus in filtered buckets.
def wrapLogs(self)
-
Expand source code Browse git
def wrapLogs(self): """HTML for the log messages produced when processing a sheet.""" v = self.v ner = self.ner sheetData = ner.getSheetData() logData = sheetData.logData or [] v.logs = "\n".join( H.p( H.nb * indent + msg, cls="msg " + ("special" if isError is None else "error" if isError else "info"), ) for (isError, indent, msg) in logData )
HTML for the log messages produced when processing a sheet.
def wrapMessages(self)
-
Expand source code Browse git
def wrapMessages(self): """HTML for messages.""" v = self.v messageSrc = v.messagesrc v.messages = H.p((H.span(text, cls=lev) + H.br() for (lev, text) in messageSrc))
HTML for messages.
def wrapQuery(self)
-
Expand source code Browse git
def wrapQuery(self): """HTML for all control widgets on the page.""" self.wrapAppearance() self.wrapFilter() self.wrapEntity() self.wrapEntityText() self.wrapScope() self.wrapEntityFeats() self.wrapEntityModReport() self.wrapEntityModify()
HTML for all control widgets on the page.
def wrapReport(self, report, kind)
-
Expand source code Browse git
def wrapReport(self, report, kind): """HTML for the report of add / del actions.""" v = self.v ner = self.ner settings = ner.settings features = settings.features label = "Deletion" if kind == "del" else "Addition" if kind == "add" else "" v[f"report{kind}"] = H.join( H.div( ( H.join( H.div(f"{label}: {n} x {valRep(features, fVals)}") for (fVals, n) in line ) if type(line) is tuple else line ), cls="report", ) for line in report ) report.clear()
HTML for the report of add / del actions.
def wrapScope(self)
-
Expand source code Browse git
def wrapScope(self): """HTML for the scope widget. The scope widget lets the user choose whether the add / del actions should be applied to all relevant buckets, or only to the filtered buckets. """ v = self.v ner = self.ner setIsRo = ner.setIsRo scope = v.scope hasFilter = v.hasfilter txt = v.txt eTxt = v.etxt hasOcc = txt != "" hasEnt = eTxt != "" scopeInit = H.input(type="hidden", id="scope", name="scope", value=scope) scopeFilter = "" if (not setIsRo) and (hasOcc or hasEnt): # Scope of modification scopeFilter = ( H.span( H.button("", type="button", id="scopebutton", title="", cls="alt") ) if hasFilter else "" ) v.scopeinit = scopeInit v.scopefilter = scopeFilter
HTML for the scope widget.
The scope widget lets the user choose whether the add / del actions should be applied to all relevant buckets, or only to the filtered buckets.
def wrapSets(self)
-
Expand source code Browse git
def wrapSets(self): """HTML for the annotation task chooser. It is a dropdown with options, each corresponding to an existing annotation task. There is also a control to delete the task. Apart from these buttons there is a button to switch to the entities that are present in the TF dataset as nodes of the entity type specified in the YAML file with corresponding features. Finally, it is possible to create a new, empty annotation task. """ ner = self.ner taskNames = ner.getTasks() v = self.v chosenTask = v.task sheetCase = v.sheetcase content1 = [ H.input( type="hidden", name="task", value=chosenTask, id="seth", ), H.input( type="hidden", name="duset", value="", id="duseth", ), H.input( type="hidden", name="rset", value="", id="rseth", ), H.button( "+", type="submit", id="anew", title="create a new annotation task", cls="mono", ), " ", H.button( "++", type="submit", id="adup", title="duplicate this annotation task", cls="mono", ), " ", H.select( ( H.option( ner.setInfo(taskName)[0], value=taskName, selected=taskName == chosenTask, ) for taskName in [""] + sorted(taskNames) ), cls="selinp", id="achange", ), " ", H.input( type="hidden", id="sheetcase", name="sheetcase", value="v" if sheetCase else "x", ), H.button( "C" if sheetCase else "¢", type="submit", id="sheetcasebutton", title="toggle case-sensitivity", cls="mono", ), ] content2 = ( [ H.input( type="hidden", name="dset", value="", id="dseth", ), H.button( "→", type="submit", id="arename", title="rename current annotation task", cls="mono", ), " ", H.button( "-", type="submit", id="adelete", title="delete current annotation task", cls="mono", ), ] if not (chosenTask == "" or chosenTask.startswith(".")) else [] ) v.tasks = H.p(content1, content2)
HTML for the annotation task chooser.
It is a dropdown with options, each corresponding to an existing annotation task. There is also a control to delete the task.
Apart from these buttons there is a button to switch to the entities that are present in the TF dataset as nodes of the entity type specified in the YAML file with corresponding features.
Finally, it is possible to create a new, empty annotation task.
def wrapSubtle(self)
-
Expand source code Browse git
def wrapSubtle(self): """HTML for the button to filter the entity list by scoped/nonscoped triggers.""" v = self.v subtleFilter = v.subtlefilter ner = self.ner setIsX = ner.setIsX return H.span( H.input( type="hidden", name="subtlefilter", id="subtlefilter", value="" if subtleFilter is None else "v" if subtleFilter else "x", ), ( "" if not setIsX else H.button( ( "all scopes" if subtleFilter is None else "scoped" if subtleFilter else "unscoped" ), type="submit", id="subtlefilterbutton", cls="mono", ) ), cls="subtlefilter", )
HTML for the button to filter the entity list by scoped/nonscoped triggers.