diff --git a/app/__init__.py b/app/__init__.py index d9311c9a9fd9011306bcdbfa985420c4a957a42f..145f2b3eef971455974eef321c849b398f8c25c3 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -73,4 +73,7 @@ def create_app(config: Config = Config) -> Flask: from .settings import bp as settings_blueprint app.register_blueprint(settings_blueprint, url_prefix='/settings') + from .test import bp as test_blueprint + app.register_blueprint(test_blueprint, url_prefix='/test') + return app diff --git a/app/static/js/CorpusAnalysis/QueryBuilder.js b/app/static/js/CorpusAnalysis/QueryBuilder.js new file mode 100644 index 0000000000000000000000000000000000000000..0fe8d50c10ea9bbe2046b3df9caedeeda44b0d0c --- /dev/null +++ b/app/static/js/CorpusAnalysis/QueryBuilder.js @@ -0,0 +1,609 @@ +class ConcordanceQueryBuilder { + + constructor() { + + + this.positionalAttrList = { + 'emptyToken': {prettyText: 'empty token', cqlOpening: '[', tokenValue:'', cqlClosing: ']'}, + 'word': {prettyText: 'word', cqlOpening: '[word=', tokenValue: '', cqlClosing: ']'}, + 'lemma': {prettyText: 'lemma', cqlOpening: '[lemma=', tokenValue:'', cqlClosing: ']'}, + 'pos': {prettyText: 'pos', cqlOpening: '[pos=', tokenValue:'', cqlClosing: ']'}, + 'simplePos': {prettyText: 'simple_pos', cqlOpening: '[simple_pos=', tokenValue:'', cqlClosing: ']'} + } + + this.elements = { + + counter: 0, + yourQueryContent: [], + queryContent:[], + + //#region QueryBuilder Elements + concordanceQueryBuilder: document.querySelector('#concordance-query-builder'), + concordanceQueryBuilderButton: document.querySelector('#concordance-query-builder-button'), + positionalAttr: document.querySelector('#token-attr'), + structuralAttr: document.querySelector('#structural-attr'), + buttonPreparer: document.querySelector('#button-preparer'), + yourQuery: document.querySelector('#your-query'), + insertQueryButton: document.querySelector('#insert-query-button'), + tokenQuery: document.querySelector('#token-query'), + tokenBuilderContent: document.querySelector('#token-builder-content'), + buildTokenButton: document.querySelector('#build-token-button'), + + + //#endregion QueryBuilder Elements + + //#region Strucutral Attributes + + sentence:document.querySelector('#sentence'), + entity: document.querySelector('#entity'), + textAnnotation: document.querySelector('#text-annotation'), + + entityBuilder: document.querySelector('#entity-builder'), + englishEntType: document.querySelector('#english-ent-type'), + emptyEntity: document.querySelector('#empty-entity'), + + textAnnotationBuilder: document.querySelector('#text-annotation-builder'), + textAnnotationOptions: document.querySelector('#text-annotation-options'), + textAnnotationInput: document.querySelector('#text-annotation-input'), + textAnnotationSubmit: document.querySelector('#text-annotation-submit'), + //#endregion Structural Attributes + + //#region Token Attributes + tokenCounter: 0, + + lemma: document.querySelector('#lemma'), + emptyToken: document.querySelector('#empty-token'), + word: document.querySelector('#word'), + lemma: document.querySelector('#lemma'), + pos: document.querySelector('#pos'), + simplePosButton: document.querySelector('#simple-pos-button'), + incidenceModifiers: document.querySelector('[data-target="incidence-modifiers"]'), + or: document.querySelector('#or'), + and: document.querySelector('#and'), + + + //#region Word and Lemma Elements + wordBuilder: document.querySelector('#word-builder'), + lemmaBuilder: document.querySelector('#lemma-builder'), + inputOptions: document.querySelector('#input-options'), + wordInput: document.querySelector('#word-input'), + wordSubmit: document.querySelector('#word-submit'), + lemmaInput: document.querySelector('#lemma-input'), + lemmaSubmit: document.querySelector('#lemma-submit'), + ignoreCaseCheckbox : document.querySelector('#ignore-case-checkbox'), + ignoreCase: document.querySelector('input[type="checkbox"]'), + wildcardChar: document.querySelector('#wildcard-char'), + optionGroup: document.querySelector('#option-group'), + incidenceModifiersTB: document.querySelector('[data-target="incidence-modifiers-text-builder"]'), + //#endregion Word and Lemma Elements + + //#region posBuilder Elements + posBuilder: document.querySelector('#pos-builder'), + englishPos: document.querySelector('#english-pos'), + germanPos: document.querySelector('#german-pos'), + //#endregion posBuilder Elements + + //#region simple_posBuilder Elements + simplePosBuilder: document.querySelector('#simplepos-builder'), + simplePos: document.querySelector('#simple-pos'), + //#endregion simple_posBuilder Elements + + //#region incidence modifiers + oneOrMore: document.querySelector('#one-or-more'), + zeroOrMore: document.querySelector('#zero-or-more'), + zeroOrOne: document.querySelector('#zero-or-one'), + exactlyN: document.querySelector('#exactly-n'), + betweenNM: document.querySelector('#between-n-m'), + + oneOrMoreTB: document.querySelector('#one-or-more-tb'), + zeroOrMoreTB: document.querySelector('#zero-or-more-tb'), + zeroOrOneTB: document.querySelector('#zero-or-one-tb'), + exactlyNTB: document.querySelector('#exactly-n-tb'), + betweenNMTB: document.querySelector('#between-n-m-tb') + //#endregion incidence modifiers + + //#endregion Token Attributes + } + + this.elements.concordanceQueryBuilderButton.addEventListener('click', () => {this.clearAll();}); + + //#region Structural Attribute Event Listeners + this.elements.sentence.addEventListener('click', () => {this.addSentence();}); + this.elements.entity.addEventListener('click', () => {this.addEntity();}); + this.elements.textAnnotation.addEventListener('click', () => {this.addTextAnnotation();}); + + this.elements.englishEntType.addEventListener('change', () => {this.englishEntTypeHandler();}); + this.elements.emptyEntity.addEventListener('click', () => {this.emptyEntityButton();}); + + this.elements.textAnnotationSubmit.addEventListener('click', () => {this.textAnnotationSubmitHandler();}); + + //#endregion + + //#region Token Attribute Event Listeners + this.elements.buildTokenButton.addEventListener('click', () => {this.addToken();}); + this.elements.emptyToken.addEventListener('click', () => {this.emptyTokenHandler();}); + this.elements.word.addEventListener('click', () => {this.wordBuilder();}); + this.elements.lemma.addEventListener('click', () => {this.lemmaBuilder();}); + this.elements.pos.addEventListener('click', () => {this.posBuilder();}); + this.elements.simplePosButton.addEventListener('click', () => {this.simplePosBuilder();}); + this.elements.or.addEventListener('click', () => {this.orHandler();}); + this.elements.and.addEventListener('click', () => {this.andHandler();}); + + this.elements.ignoreCase.addEventListener('change', () => {this.inputOptionHandler(this.elements.ignoreCase);}); + this.elements.wildcardChar.addEventListener('click', () => {this.inputOptionHandler(this.elements.wildcardChar);}); + this.elements.optionGroup.addEventListener('click', () => {this.inputOptionHandler(this.elements.optionGroup);}); + this.elements.wordSubmit.addEventListener('click', () => {this.textSubmit();}); + this.elements.lemmaSubmit.addEventListener('click', () => {this.textSubmit();}); + + this.elements.englishPos.addEventListener('change', () => {this.englishPosHandler();}); + this.elements.germanPos.addEventListener('change', () => {this.germanPosHandler();}); + this.elements.simplePos.addEventListener('change', () => {this.simplePosHandler();}); + + this.elements.oneOrMore.addEventListener('click', () => {this.incidenceModifiersHandler(this.elements.oneOrMore);}); + this.elements.zeroOrMore.addEventListener('click', () => {this.incidenceModifiersHandler(this.elements.zeroOrMore);}); + this.elements.zeroOrOne.addEventListener('click', () => {this.incidenceModifiersHandler(this.elements.zeroOrOne);}); + this.elements.exactlyN.addEventListener('click', () => {this.incidenceModifiersHandler(this.elements.exactlyN);}); + this.elements.betweenNM.addEventListener('click', () => {this.incidenceModifiersHandler(this.elements.betweenNM);}); + + this.elements.oneOrMoreTB.addEventListener('click', () => {this.incidenceModifiersHandlerTB(this.elements.oneOrMoreTB);}); + this.elements.zeroOrMoreTB.addEventListener('click', () => {this.incidenceModifiersHandlerTB(this.elements.zeroOrMoreTB);}); + this.elements.zeroOrOneTB.addEventListener('click', () => {this.incidenceModifiersHandlerTB(this.elements.zeroOrOneTB);}); + this.elements.exactlyNTB.addEventListener('click', () => {this.incidenceModifiersHandlerTB(this.elements.exactlyNTB);}); + this.elements.betweenNMTB.addEventListener('click', () => {this.incidenceModifiersHandlerTB(this.elements.betweenNMTB);}); + + + //#endregion Token Attribute Event Listeners + + + } + + //#region Structural Attribute Builder Functions + addSentence() { + this.hideEverything(); + if(this.elements.sentence.text === "End Sentence") { + this.buttonfactory("end-sentence", "Sentence End"); + this.elements.sentence.innerHTML = "Sentence"; + } else { + this.buttonfactory("start-sentence", "Sentence Start"); + this.elements.insertQueryButton.classList.remove('disabled'); + this.elements.counter += 1; + this.elements.queryContent.push('sentence'); + this.elements.sentence.innerHTML = "End Sentence"; + } + } + + addEntity() { + if(this.elements.entity.text === "End Entity"){ + this.buttonfactory("end-entity", "Entity End"); + this.elements.entity.innerHTML = "Entity"; + + } else { + this.hideEverything(); + this.elements.entityBuilder.classList.remove('hide'); + } + } + + englishEntTypeHandler() { + + this.buttonfactory("start-entity", "Entity Type=" + this.elements.englishEntType.value); + this.elements.insertQueryButton.classList.remove('disabled'); + this.elements.counter+=1; + this.elements.entity.innerHTML = "End Entity"; + this.hideEverything(); + + } + + emptyEntityButton() { + this.buttonfactory("start-entity", "Entity Start"); + this.elements.counter+=1; + this.elements.entity.innerHTML = "End Entity"; + this.hideEverything(); + } + + addTextAnnotation() { + this.hideEverything(); + this.elements.textAnnotationBuilder.classList.remove('hide'); + + } + + textAnnotationSubmitHandler() { + this.buttonfactory("tA" + this.elements.textAnnotationOptions.value, this.elements.textAnnotationOptions.value + "= " + this.elements.textAnnotationInput.value) + this.elements.counter+=1; + this.hideEverything(); + this.elements.textAnnotationInput.value = ""; + } + + + //#endregion Structural Attribute Builder Functions + + //#region Token Attribute Builder Functions + emptyTokenHandler() { + this.hideEverything(); + this.elements.incidenceModifiers.classList.remove("hide"); + this.tokenButtonfactory("emptyToken", "empty token"); + this.buttonDisabler("empty"); + this.elements.buildTokenButton.classList.remove('disabled'); + this.elements.tokenCounter+=1; + } + + wordBuilder() { + this.elements.incidenceModifiers.classList.add("hide"); + this.hideEverything(); + this.elements.wordBuilder.classList.remove('hide'); + this.elements.inputOptions.classList.remove('hide'); + this.elements.ignoreCaseCheckbox.classList.remove('hide'); + } + + lemmaBuilder() { + this.elements.incidenceModifiers.classList.add("hide"); + this.hideEverything(); + this.elements.lemmaBuilder.classList.remove('hide'); + this.elements.inputOptions.classList.remove('hide'); + this.elements.ignoreCaseCheckbox.classList.remove('hide'); + } + + inputOptionHandler(elem) { + let input; + + if (this.elements.wordBuilder.classList.contains("hide") === false){ + input = this.elements.wordInput; + }else{ + input = this.elements.lemmaInput; + } + + if (elem === this.elements.optionGroup) { + input.value += "( option1 | option2 )"; + let firstIndex = input.value.indexOf("option1"); + let lastIndex = firstIndex + "option1".length; + input.focus(); + input.setSelectionRange(firstIndex, lastIndex); + }else if (elem === this.elements.wildcardChar){ + input.value += "."; + } + } + + textSubmit() { + this.buttonDisabler(); + this.elements.incidenceModifiers.classList.remove('disabled'); + let c; + + if (this.elements.ignoreCase.checked){ + c = "%c"; + }else{ + c = ""; + } + + if (this.elements.wordBuilder.classList.contains("hide") === false){ + this.tokenButtonfactory("word", "word=" + this.elements.wordInput.value + c); + this.elements.buildTokenButton.classList.remove('disabled'); + this.elements.tokenCounter+=1; + this.elements.wordInput.value = ""; + }else if (this.elements.lemmaBuilder.classList.contains("hide") === false){ + this.tokenButtonfactory("lemma", "lemma=" + this.elements.lemmaInput.value + c); + this.elements.buildTokenButton.classList.remove('disabled'); + this.elements.tokenCounter+=1; + this.elements.lemmaInput.value = ""; + } + } + + posBuilder() { + this.hideEverything(); + this.elements.incidenceModifiers.classList.remove("hide"); + this.elements.positionalAttr.appendChild(this.elements.incidenceModifiers); + this.elements.posBuilder.classList.remove('hide'); + } + + englishPosHandler() { + this.buttonDisabler(); + this.tokenButtonfactory("pos", "pos=" + this.elements.englishPos.value); + this.elements.buildTokenButton.classList.remove('disabled'); + this.elements.tokenCounter+=1; + } + + germanPosHandler() { + this.buttonDisabler(); + this.tokenButtonfactory("pos", "pos=" + this.elements.germanPos.value); + this.elements.buildTokenButton.classList.remove('disabled'); + this.elements.tokenCounter+=1; + } + + simplePosBuilder() { + this.hideEverything(); + this.elements.incidenceModifiers.classList.remove("hide"); + this.elements.positionalAttr.appendChild(this.elements.incidenceModifiers); + this.elements.simplePosBuilder.classList.remove('hide'); + } + + simplePosHandler() { + this.buttonDisabler(); + this.tokenButtonfactory("simplePos", "simple_pos=" + this.elements.simplePos.value); + this.elements.buildTokenButton.classList.remove('disabled'); + this.elements.tokenCounter+=1; + } + + incidenceModifiersHandler(input) { + if (input.id === "exactly-n"){ + + } else if (input.id === "between-n-m"){ + + } else { + this.tokenButtonfactory("incidenceModifier", input.text); + } + this.elements.incidenceModifiers.classList.add('disabled'); + this.elements.tokenCounter+=1; + } + + incidenceModifiersHandlerTB(elem) { + let input; + + if (this.elements.wordBuilder.classList.contains("hide") === false){ + input = this.elements.wordInput; + }else{ + input = this.elements.lemmaInput; + } + + if (elem.id === "exactly-n-tb"){ + input.value += elem.dataset.token; + let index = input.value.lastIndexOf("{n}"); + let instance = M.Dropdown.getInstance(this.elements.incidenceModifiersTB); + instance.close(); + input.focus(); + input.setSelectionRange(index+1, index+2); + }else if (elem.id === "between-n-m-tb") { + input.value += input.dataset.token; + let index = input.value.lastIndexOf("{n,m}"); + let instance = M.Dropdown.getInstance(this.elements.incidenceModifiersTB); + instance.close(); + input.focus(); + input.setSelectionRange(index+1, index+2); + }else { + input.value += " " + elem.dataset.token; + } + } + + orHandler() { + this.elements.positionalAttr.appendChild(this.elements.incidenceModifiers); + this.buttonDisabler("condition"); + this.hideEverything(); + this.tokenButtonfactory("or", "or"); + this.elements.buildTokenButton.classList.remove('disabled'); + this.elements.tokenCounter+=1; + } + + andHandler() { + this.elements.positionalAttr.appendChild(this.elements.incidenceModifiers); + this.buttonDisabler("condition"); + this.hideEverything(); + this.tokenButtonfactory("and", "and"); + this.elements.buildTokenButton.classList.remove('disabled'); + this.elements.tokenCounter+=1; + } + + hideEverything(){ + + this.elements.wordBuilder.classList.add('hide'); + this.elements.lemmaBuilder.classList.add('hide'); + this.elements.ignoreCaseCheckbox.classList.add('hide'); + this.elements.inputOptions.classList.add('hide'); + this.elements.posBuilder.classList.add('hide'); + this.elements.simplePosBuilder.classList.add('hide'); + this.elements.entityBuilder.classList.add('hide'); + this.elements.textAnnotationBuilder.classList.add('hide'); + } + + buttonDisabler(attr = "token") { + let tokenButtonList = this.elements.positionalAttr.querySelectorAll('a'); + if (attr === "start"){ + tokenButtonList.forEach(element => { + if (element.id === "or"){ + element.classList.add('disabled'); + } else if (element.id === "and"){ + element.classList.add('disabled'); + }else if (element.dataset.target == "incidence-modifiers") { + element.classList.add('disabled'); + } else if (element.id === "empty-token"){ + element.classList.remove('disabled'); + } else { + element.classList.remove('disabled'); + } + }); + }else if (attr === "condition"){ + tokenButtonList.forEach(element => { + if (element.id === "or"){ + element.classList.add('disabled'); + } else if (element.id === "and"){ + element.classList.add('disabled'); + }else if (element.dataset.target == "incidence-modifiers") { + element.classList.add('disabled'); + } else if (element.id === "empty-token"){ + element.classList.add('disabled'); + } else { + element.classList.remove('disabled'); + } + }); + }else if (attr === "empty") { + tokenButtonList.forEach(element => { + if (element.id == "or"){ + element.classList.add('disabled'); + } else if (element.id == "and"){ + element.classList.add('disabled'); + }else if (element.dataset.target == "incidence-modifiers") { + element.classList.remove('disabled'); + } else { + element.classList.add('disabled'); + } + }); + }else{ + tokenButtonList.forEach(element => { + if (element.id == "or"){ + element.classList.remove('disabled'); + } else if (element.id == "and"){ + element.classList.remove('disabled'); + }else if (element.dataset.target == "incidence-modifiers") { + element.classList.remove('disabled'); + } else { + element.classList.add('disabled'); + } + }); + } + + } + + tokenButtonfactory(dataType, prettyText) { + this.elements.buttonPreparer.innerHTML += "<a class='btn-small waves-effect waves-light' style='margin-left:3px' data-type='" + dataType + "'>" + prettyText + "</a>"; + let tokenDummyButton = this.elements.buttonPreparer.querySelector(":first-child"); + tokenDummyButton.addEventListener('click', () => {this.deleteTokenAttr(tokenDummyButton);}); + this.elements.tokenQuery.appendChild(tokenDummyButton); + } + + deleteTokenAttr(attr){ + let nodesList = attr.parentElement.childNodes; + let indexOfAttr; + + for (let i = 0; i < nodesList.length; i++) { + if(nodesList[i] === attr){ + indexOfAttr = i; + } + } + + if(indexOfAttr>0){ + if (attr.dataset.type === "or" || attr.dataset.type === "and") { + this.elements.tokenQuery.removeChild(nodesList[indexOfAttr+1]); + this.elements.tokenCounter-=1; + + }else { + if (nodesList[indexOfAttr-1].dataset.type === "or" || nodesList[indexOfAttr-1].dataset.type === "and"){ + this.elements.tokenQuery.removeChild(nodesList[indexOfAttr-1]) + this.elements.tokenCounter-=1; + } + } + } + + this.elements.tokenQuery.removeChild(attr); + this.elements.tokenCounter-=1; + + if(this.elements.tokenCounter === 0){ + this.elements.buildTokenButton.classList.add('disabled'); + this.buttonDisabler("start"); + this.hideEverything(); + } + } + + addToken() { + let tokenQueryContent = ""; + this.elements.tokenQuery.childNodes.forEach(element => { + tokenQueryContent += " " + element.text + " " + }); + this.buttonfactory("token", tokenQueryContent); + tokenQueryContent = ""; + this.elements.tokenQuery.innerHTML = ""; + this.elements.tokenCounter = 0; + this.elements.buildTokenButton.classList.add('disabled'); + this.hideEverything(); + this.buttonDisabler("start") + } + + //#endregion Token Attribute Builder Functions + + //#region General Functions + + buttonfactory(dataType, prettyText) { + this.elements.buttonPreparer.innerHTML += "<a class='btn-small waves-effect waves-light' style='margin-left:3px' data-type='" + dataType + "'>" + prettyText + "</a>"; + let dummyButton = this.elements.buttonPreparer.querySelector(":first-child"); + dummyButton.addEventListener('click', () => {this.deleteAttr(dummyButton);}); + this.elements.yourQuery.appendChild(dummyButton); + } + + deleteAttr(attr) { + + let siblingList = []; + let nodesList = attr.parentElement.childNodes; + let indexOfAttr; + let connectedElement; + + // Why nodesList.indexOf(attr) doesn't work?! + for (let i = 0; i < nodesList.length; i++) { + if(nodesList[i] === attr){ + indexOfAttr = i; + } + } + switch (attr.dataset.type) { + case "start-sentence": + for (let i = indexOfAttr; i < nodesList.length; i++) { + if (nodesList[i].dataset.type === "end-sentence"){ + siblingList.push(nodesList[i]); + } + } + connectedElement = siblingList[0]; + break; + case "end-sentence": + for (let i = 0; i < indexOfAttr; i++) { + if (nodesList[i].dataset.type === "start-sentence"){ + siblingList.push(nodesList[i]); + } + } + connectedElement = siblingList[siblingList.length -1]; + break; + case "start-entity": + for (let i = indexOfAttr; i < nodesList.length; i++) { + if (nodesList[i].dataset.type === "end-entity"){ + siblingList.push(nodesList[i]); + } + } + connectedElement = siblingList[0]; + break; + case "end-entity": + for (let i = 0; i < indexOfAttr; i++) { + if (nodesList[i].dataset.type === "start-entity"){ + siblingList.push(nodesList[i]); + } + } + connectedElement = siblingList[siblingList.length -1]; + break; + default: + connectedElement = ""; + break; + } + + if (connectedElement !== ""){ + this.elements.yourQuery.removeChild(connectedElement); + } + this.elements.yourQuery.removeChild(attr); + + this.elements.counter -= 1; + if(this.elements.counter === 0){ + this.elements.insertQueryButton.classList.add('disabled'); + + } + } + + insertQuery() { + + + // this.elements.tokenQuery.childNodes.forEach(element => { + // this.elements.tokenQueryContent.push([element.dataset.type, element.text]); + // }); + // for (let key in this.positionalAttrList) { + // if (this.positionalAttrList.hasOwnProperty(key)){ + // for (let i = 0; i < this.elements.tokenQueryContent.length; i++) { + // if(key === this.elements.tokenQueryContent[i][0]){ + // console.log(this.positionalAttrList[key]['prettyText']); + // } + // } + // } + // } + } + + clearAll() { + this.elements.tokenQuery.innerHTML = ""; + this.elements.tokenCounter = 0; + this.elements.counter = 0; + this.elements.buildTokenButton.classList.add('disabled'); + this.elements.insertQueryButton.classList.add('disabled'); + this.hideEverything(); + this.buttonDisabler("start"); + this.elements.yourQuery.innerHTML = ""; + } + //#endregion General Functions +} + diff --git a/app/templates/_scripts.html.j2 b/app/templates/_scripts.html.j2 index 89a672dcc5a7c9f15b24d7ac6be86cae460adfc6..54095e0018c4416b6d8eb92c73602954a54eb850 100644 --- a/app/templates/_scripts.html.j2 +++ b/app/templates/_scripts.html.j2 @@ -9,6 +9,7 @@ 'js/CorpusAnalysis/CorpusAnalysisApp.js', 'js/CorpusAnalysis/CorpusAnalysisConcordance.js', 'js/CorpusAnalysis/CorpusAnalysisReader.js', + 'js/CorpusAnalysis/QueryBuilder.js', 'js/JobStatusNotifier.js', 'js/RessourceDisplays/RessourceDisplay.js', 'js/RessourceDisplays/CorpusDisplay.js', diff --git a/app/templates/test/analyse_corpus.concordance.html.j2 b/app/templates/test/analyse_corpus.concordance.html.j2 new file mode 100644 index 0000000000000000000000000000000000000000..31320f70d2346e4de66b12843920700e79b5d082 --- /dev/null +++ b/app/templates/test/analyse_corpus.concordance.html.j2 @@ -0,0 +1,117 @@ +<div class="row" id="concordance-extension-container"> + <div class="col s12"> + <div class="card"> + <div class="card-content"> + <form id="concordance-extension-form"> + <div class="row"> + <div class="input-field col s12 m9"> + <i class="material-icons prefix">search</i> + <input class="validate corpus-analysis-action" id="concordance-extension-form-query" name="query" type="text" + required pattern=".*\S+.*" + placeholder="<ent_type="PERSON"> []* </ent_type> []* [simple_pos="VERB"] :: match.text_publishing_year="1991";"> + </input> + <label for="concordance-extension-form-query">Query</label> + <span class="error-color-text helper-text hide" id="concordance-extension-error"></span> + <a class="modal-trigger" href="#cql-tutorial-modal" style="margin-left: 40px;"><i class="material-icons" style="font-size: inherit;">help</i> + Corpus Query Language tutorial</a> + <span> | </span> + <a class="modal-trigger" href="#tagsets-modal"><i class="material-icons" style="font-size: inherit;">info</i> Tagsets</a> + </div> + <div class="input-field col s12 m3"> + <i class="material-icons prefix">arrow_forward</i> + <input class="validate corpus-analysis-action" id="concordance-extension-form-subcorpus-name" name="subcorpus-name" type="text" + required pattern="^[A-Z][a-z0-9\-]*" value="Last"> + </input> + <label for="concordance-extension-form-subcorpus-name">Subcorpus name</label> + </div> + <div class="col s12 m9 l9"> + <div class="row"> + <div class="input-field col s4 l3"> + <i class="material-icons prefix">short_text</i> + <select class="corpus-analysis-action" name="context"> + <option value="10" selected>10</option> + <option value="15">15</option> + <option value="20">20</option> + <option value="25">25</option> + <option value="30">30</option> + </select> + <label>Context</label> + </div> + <div class="input-field col s4 l3"> + <i class="material-icons prefix">format_list_numbered</i> + <select class="corpus-analysis-action" name="per-page"> + <option value="10" selected>10</option> + <option value="15">15</option> + <option value="20">20</option> + <option value="25">25</option> + </select> + <label>Matches per page</label> + </div> + <div class="input-field col s4 l3"> + <i class="material-icons prefix">format_shapes</i> + <select name="text-style"> + <option value="0">Plain text</option> + <option value="1" selected>Highlight entities</option> + <option value="2">Token text</option> + </select> + <label>Text style</label> + </div> + <div class="input-field col s4 l3"> + <i class="material-icons prefix">format_quote</i> + <select name="token-representation"> + <option value="lemma">lemma</option> + <option value="pos">pos</option> + <option value="simple_pos">simple_pos</option> + <option value="word" selected>word</option> + </select> + <label>Token representation</label> + </div> + </div> + </div> + <div class="col s12 m3 l3 right-align"> + <p class="hide-on-small-only"> </p> + <a class="btn waves-effect waves-light modal-trigger" href="#concordance-query-builder" id="concordance-query-builder-button"> + <i class="material-icons left">build</i> + Query builder + </a> + <button class="btn waves-effect waves-light corpus-analysis-action" id="concordance-extension-form-submit" type="submit" name="submit"> + Send + <i class="material-icons right">send</i> + </button> + </div> + </div> + </form> + </div> + </div> + </div> + + <div class="col s12"> + <div id="concordance-extension-subcorpus-list"></div> + + <div class="card"> + <div class="card-content"> + <div class="progress hide" id="concordance-extension-progress"> + <div class="indeterminate"></div> + </div> + <div class="row"> + <div class="col s9"><p class="hide" id="concordance-extension-subcorpus-info"></p></div> + <div class="col s3 right-align" id="concordance-extension-subcorpus-actions"></div> + </div> + <table class="highlight"> + <thead> + <tr> + <th style="width: 2%;"></th> + <th style="width: 8%;">Source</th> + <th class="right-align" style="width: 22.5%;">Left context</th> + <th class="center-align" style="width: 40%;">KWIC</th> + <th class="left-align" style="width: 22.5%;">Right Context</th> + <th class="left-align" style="width: 5%;"></th> + </tr> + </thead> + <tbody id="concordance-extension-subcorpus-items"></tbody> + </table> + <ul class="pagination hide" id="concordance-extension-subcorpus-pagination"></ul> + </div> + </div> + </div> +</div> diff --git a/app/templates/test/analyse_corpus.html.j2 b/app/templates/test/analyse_corpus.html.j2 new file mode 100644 index 0000000000000000000000000000000000000000..8a1013056d346513c79bcaca59e9102de65c33b4 --- /dev/null +++ b/app/templates/test/analyse_corpus.html.j2 @@ -0,0 +1,511 @@ +{% extends "base.html.j2" %} +{% import "materialize/wtf.html.j2" as wtf %} + + + +{% block main_attribs %} class="service-scheme" data-service="corpus-analysis" id="corpus-analysis-app-container"{% endblock main_attribs %} + +{% block page_content %} +<ul class="row tabs no-autoinit" id="corpus-analysis-app-extension-tabs"> + <li class="tab col s3"><a href="#corpus-analysis-app-overview"><i class="nopaque-icons service-icon left" data-service="corpus-analysis"></i>Corpus analysis</a></li> + <li class="tab col s3"><a class="active" href="#concordance-extension-container"><i class="material-icons left">list_alt</i>Concordance</a></li> + <li class="tab col s3"><a href="#reader-extension-container"><i class="material-icons left">chrome_reader_mode</i>Reader</a></li> +</ul> + +{# <div class="row" id="corpus-analysis-app-overview"> + <div class="col s12"> + <h1>{{ title }}</h1> + </div> + + <div class="col s3"> + <div class="card extension-selector hoverable" data-target="concordance-extension-container"> + <div class="card-content"> + <span class="card-title"><i class="material-icons left">list_alt</i>Concordance</span> + <p>Query your corpus with the CQP query language utilizing a KWIC view.</p> + </div> + </div> + </div> + + <div class="col s3"> + <div class="card extension-selector hoverable" data-target="reader-extension-container"> + <div class="card-content"> + <span class="card-title"><i class="material-icons left">chrome_reader_mode</i>Reader</span> + <p>Inspect your corpus in detail with a full text view, including annotations.</p> + </div> + </div> + </div> +</div> #} +{% include "test/analyse_corpus.concordance.html.j2" %} +{% endblock page_content %} + +{% block modals %} +{{ super() }} +<div class="modal no-autoinit" id="corpus-analysis-app-init-modal"> + <div class="modal-content"> + <h4>Initializing session...</h4> + <p>If the loading takes to long or an error occured, + <a onclick="window.location.reload()" href="#">click here</a> + to refresh your session or + <a href="">go back</a>! + </p> + <div class="progress" id="corpus-analysis-app-init-progress"> + <div class="indeterminate"></div> + </div> + <p class="error-color-text hide" id="corpus-analysis-app-init-error"></p> + </div> +</div> + + + +<div class="modal" id="cql-tutorial-modal"> + <div class="modal-content"> + {% with headline_num=4 %} + {% include "main/manual/_08_cqp_query_language.html.j2" %} + {% endwith %} + </div> +</div> + +<div class="modal" id="tagsets-modal"> + <div class="modal-content"> + <h4>Tagsets</h4> + <ul class="tabs"> + <li class="tab"><a class="active" href="#simple_pos-tagset">simple_pos</a></li> + <li class="tab"><a href="#english-ent_type-tagset">English ent_type</a></li> + <li class="tab"><a href="#english-pos-tagset">English pos</a></li> + <li class="tab"><a href="#german-ent_type-tagset">German ent_type</a></li> + <li class="tab"><a href="#german-pos-tagset">German pos</a></li> + </ul> + + <div id="simple_pos-tagset"> + <h5>simple_pos tagset</h5> + <ul> + <li>ADJ: adjective</li> + <li>ADP: adposition</li> + <li>ADV: adverb</li> + <li>AUX: auxiliary verb</li> + <li>CONJ: coordinating conjunction</li> + <li>DET: determiner</li> + <li>INTJ: interjection</li> + <li>NOUN: noun</li> + <li>NUM: numeral</li> + <li>PART: particle</li> + <li>PRON: pronoun</li> + <li>PROPN: proper noun</li> + <li>PUNCT: punctuation</li> + <li>SCONJ: subordinating conjunction</li> + <li>SYM: symbol</li> + <li>VERB: verb</li> + <li>X: other</li> + </ul> + </div> + + <div id="english-ent_type-tagset"> + <h5>English ent_type tagset</h5> + <ul> + <li>CARDINAL: Numerals that do not fall under another type</li> + <li>DATE: Absolute or relative dates or periods</li> + <li>EVENT: Named hurricanes, battles, wars, sports events, etc.</li> + <li>FAC: Buildings, airports, highways, bridges, etc.</li> + <li>GPE: Countries, cities, states</li> + <li>LANGUAGE: Any named language</li> + <li>LAW: Named documents made into laws.</li> + <li>LOC: Non-GPE locations, mountain ranges, bodies of water</li> + <li>MONEY: Monetary values, including unit</li> + <li>NORP: Nationalities or religious or political groups</li> + <li>ORDINAL: "first" "second" etc.</li> + <li>ORG: Companies, agencies, institutions, etc.</li> + <li>PERCENT: Percentage, including "%"</li> + <li>PERSON: People, including fictional</li> + <li>PRODUCT: Objects, vehicles, foods, etc. (not services)</li> + <li>QUANTITY: Measurements, as of weight or distance</li> + <li>TIME: Times smaller than a day</li> + <li>WORK_OF_ART: Titles of books, songs, etc.</li> + </ul> + </div> + + <div id="english-pos-tagset"> + <h5>English pos tagset</h5> + <ul> + <li>ADD: email</li> + <li>AFX: affix</li> + <li>CC: conjunction, coordinating</li> + <li>CD: cardinal number</li> + <li>DT: determiner</li> + <li>EX: existential there</li> + <li>FW: foreign word</li> + <li>HYPH: punctuation mark, hyphen</li> + <li>IN: conjunction, subordinating or preposition</li> + <li>JJ: adjective</li> + <li>JJR: adjective, comparative</li> + <li>JJS: adjective, superlative</li> + <li>LS: list item marker</li> + <li>MD: verb, modal auxiliary</li> + <li>NFP: superfluous punctuation</li> + <li>NN: noun, singular or mass</li> + <li>NNP: noun, proper singular</li> + <li>NNPS: noun, proper plural</li> + <li>NNS: noun, plural</li> + <li>PDT: predeterminer</li> + <li>POS: possessive ending</li> + <li>PRP: pronoun, personal</li> + <li>PRP$: pronoun, possessive RB: adverb</li> + <li>RBR: adverb, comparative</li> + <li>RBS: adverb, superlative</li> + <li>RP: adverb, particle</li> + <li>SYM: symbol</li> + <li>TO: infinitival "to"</li> + <li>UH: interjection</li> + <li>VB: verb, base form</li> + <li>VBD: verb, past tense</li> + <li>VBG: verb, gerund or present participle</li> + <li>VBN: verb, past participle</li> + <li>VBP: verb, non-3rd person singular present</li> + <li>VBZ: verb, 3rd person singular present</li> + <li>WDT: wh-determiner</li> + <li>WP: wh-pronoun, personal</li> + <li>WP$: wh-pronoun, possessive</li> + <li>WRB: wh-adverb</li> + <li>XX: unknown</li> + <li>``: opening quotation mark</li> + <li>$: symbol, currency</li> + <li>"": closing quotation mark</li> + <li>: punctuation mark, comma</li> + <li>-LRB-: left round bracket</li> + <li>-RRB-: right round bracket</li> + <li>.: punctuation mark, sentence closer</li> + <li>:: punctuation mark, colon or ellipsis</li> + </ul> + </div> + + <div id="german-ent_type-tagset"> + <h5>German ent_type tagset</h5> + <ul> + <li>LOC: Non-GPE locations, mountain ranges, bodies of water</li> + <li>MISC: Miscellaneous entities, e.g. events, nationalities, products or works of art</li> + <li>ORG: Companies, agencies, institutions, etc.</li> + <li>PER: Named person or family.</li> + </ul> + </div> + + <div id="german-pos-tagset"> + <h5>German pos tagset</h5> + <ul> + <li>ADJA: adjective, attributive</li> + <li>ADJD: adjective, adverbial or predicative</li> + <li>ADV: adverb</li> + <li>APPO: postposition</li> + <li>APPR: preposition; circumposition left</li> + <li>APPRART: preposition with article</li> + <li>APZR: circumposition right</li> + <li>ART: definite or indefinite article</li> + <li>CARD: cardinal number</li> + <li>FM: foreign language material</li> + <li>ITJ: interjection</li> + <li>KOKOM: comparative conjunction</li> + <li>KON: coordinate conjunction</li> + <li>KOUI: subordinate conjunction with \zu\ and infinitive</li> + <li>KOUS: subordinate conjunction with sentence</li> + <li>NE: proper noun</li> + <li>NN: noun, singular or mass</li> + <li>NNE: proper noun</li> + <li>PDAT: attributive demonstrative pronoun</li> + <li>PDS: substituting demonstrative pronoun</li> + <li>PIAT: attributive indefinite pronoun without determiner</li> + <li>PIS: substituting indefinite pronoun</li> + <li>PPER: non-reflexive personal pronoun</li> + <li>PPOSAT: attributive possessive pronoun</li> + <li>PPOSS: substituting possessive pronoun</li> + <li>PRELAT: attributive relative pronoun</li> + <li>PRELS: substituting relative pronoun</li> + <li>PRF: reflexive personal pronoun</li> + <li>PROAV: pronominal adverb</li> + <li>PTKA: particle with adjective or adverb</li> + <li>PTKANT: answer particle</li> + <li>PTKNEG: negative particle</li> + <li>PTKVZ: separable verbal particle</li> + <li>PTKZU: "zu" before infinitive</li> + <li>PWAT: attributive interrogative pronoun</li> + <li>PWAV: adverbial interrogative or relative pronoun</li> + <li>PWS: substituting interrogative pronoun</li> + <li>TRUNC: word remnant</li> + <li>VAFIN: finite verb, auxiliary</li> + <li>VAIMP: imperative, auxiliary</li> + <li>VAINF: infinitive, auxiliary</li> + <li>VAPP: perfect participle, auxiliary</li> + <li>VMFIN: finite verb, modal</li> + <li>VMINF: infinitive, modal</li> + <li>VMPP: perfect participle, modal</li> + <li>VVFIN: finite verb, full</li> + <li>VVIMP: imperative, full</li> + <li>VVINF: infinitive, full</li> + <li>VVIZU: infinitive with "zu" full</li> + <li>VVPP: perfect participle, full</li> + <li>XY: non-word containing non-letter</li> + <li>$(: other sentence-internal punctuation mark</li> + <li>$,: comma</li> + <li>$.: sentence-final punctuation mark</li> + </ul> + </div> + </div> +</div> + + +<div class="modal" id="concordance-query-builder" style="width:70%;"> + <div class="modal-content"> + <h4>Query-Builder</h4> + <ul class="tabs"> + <li class="tab"><a class="active" href="#structural-attr">Add structural attributes</a></li> + <li class="tab"><a href="#positional-attr">Add new token</a></li> + </ul> + + <div id="structural-attr"> + <p></p> + <a class="btn-small waves-effect waves-light" id="sentence">sentence</a> + <a class="btn-small waves-effect waves-light" id="entity">entity</a> + <a class="btn-small waves-effect waves-light" id="text-annotation">text annotation</a> + + <div id="entity-builder" class="hide"> + <p></p> + <br> + <div class="row"> + <a class="btn waves-effect waves-light col s3 l2" id="empty-entity">Add Entity of any type</a> + <p class="col s1 l1"></p> + <div class= "input-field col s4 l3"> + <select name="englishenttype" id="english-ent-type"> + <option value="" disabled selected>English ent_type</option> + <option value="CARDINAL">CARDINAL</option> + <option value="DATE">DATE</option> + <option value="EVENT">EVENT</option> + </select> + <label>Entity Type</label> + </div> + </div> + </div> + + + <div id="text-annotation-builder" class="hide"> + <p></p> + <br> + <div class="row"> + <div class= "input-field col s4 l3"> + <select name="text-annotation-options" id="text-annotation-options"> + <option class="btn-small waves-effect waves-light" value="address">address</option> + <option class="btn-small waves-effect waves-light" value="author">author</option> + <option class="btn-small waves-effect waves-light" value="booktitle">booktitle</option> + <option class="btn-small waves-effect waves-light" value="chapter">chapter</option> + <option class="btn-small waves-effect waves-light" value="editor">editor</option> + <option class="btn-small waves-effect waves-light" value="institution">institution</option> + <option class="btn-small waves-effect waves-light" value="journal">journal</option> + <option class="btn-small waves-effect waves-light" value="pages">pages</option> + <option class="btn-small waves-effect waves-light" value="publisher">publisher</option> + <option class="btn-small waves-effect waves-light" value="publishing year">publishing year</option> + <option class="btn-small waves-effect waves-light" value="school">school</option> + <option class="btn-small waves-effect waves-light" value="title">title</option> + </select> + <label>text annotation</label> + </div> + <div class= "input-field col s7 l5"> + <i class="material-icons prefix">mode_edit</i> + <input placeholder="Type in your text annotation" type="text" id="text-annotation-input"> + </div> + <div class="col s1 l1 center-align"> + <p class="btn-floating waves-effect waves-light" id="text-annotation-submit"> + <i class="material-icons right">send</i> + </p> + </div> + + </div> + </div> + </div> + + {# #region test #} + + <div id="positional-attr"> + <p></p> + <div class="row"> + <div id="token-attr" class="col s12"> + <a class="btn-small waves-effect waves-light" id="empty-token" style="background-color:#43c6fc">empty token</a> + <a class="btn-small waves-effect waves-light" id="word" style="background-color:#ef60b4">word</a> + <a class="btn-small waves-effect waves-light" id="lemma" style="background-color:#ef60b4">lemma</a> + <a class="btn-small waves-effect waves-light" id="pos" style="background-color:#ef60b4">pos</a> + <a class="btn-small waves-effect waves-light" id="simple-pos-button" style="background-color:#ef60b4">simple_pos</a> + <a class="btn-small waves-effect waves-light disabled" id="or" style="background-color:#fc0">or</a> + <a class="btn-small waves-effect waves-light disabled" id="and" style="background-color:#fc0">and</a> + <a class="dropdown-trigger btn-small disabled waves-effect waves-light" href="#" data-target="incidence-modifiers" style="background-color:#2fbbab">incidence modifiers</a> + </div> + </div> + <div id="token-builder-content"> + {# #endregion test #} + + + <div class="row"> + + + <div id="word-builder" class="hide"> + <div class= "input-field col s3 l4"> + <i class="material-icons prefix">mode_edit</i> + <input placeholder="Type in your word" type="text" id="word-input"> + </div> + <div class="col s1 l1 center-align"> + <p class="btn-floating waves-effect waves-light" id="word-submit"> + <i class="material-icons right">send</i> + </p> + </div> + </div> + + <div id="lemma-builder" class="hide" > + <div class= "input-field col s3 l4"> + <i class="material-icons prefix">mode_edit</i> + <input placeholder="Type in your lemma" type="text" id="lemma-input"> + </div> + <div class="col s1 l1 center-align"> + <p class="btn-floating waves-effect waves-light" id="lemma-submit"> + <i class="material-icons right">send</i> + </p> + </div> + </div> + + <div id="input-options" class="col s6 l4 hide center-align"> + <p id="wildcard-char" class="btn-small tooltipped waves-effect waves-light" data-position="top" data-tooltip="Wildcard Character">.</p> + <p id="option-group" class="btn-small waves-effect waves-light">Option Group</p> + <a class="dropdown-trigger btn-small waves-effect waves-light" href="#" data-target="incidence-modifiers-text-builder" style="background-color:#2fbbab">incidence modifiers</a> + </div> + + <div id="ignore-case-checkbox" class="hide col s2 l3"> + <p id="ignore-case"> + <label> + <input type="checkbox" class="filled-in" /> + <span>Ignore Case</span> + </label> + </p> + </div> + </div> + + <ul id="incidence-modifiers-text-builder" class="dropdown-content"> + <li><a id="one-or-more-tb" data-token="+">one or more</a></li> + <li><a id="zero-or-more-tb" data-token="*">zero or more</a></li> + <li><a id="zero-or-one-tb" data-token="?">zero or one</a></li> + <li><a id="exactly-n-tb" data-token="{n}">exactly n</a></li> + <li><a id="between-n-m-tb" data-token="{n,m}" >between n and m</a></li> + </ul> + + + <div id="pos-builder" class="hide"> + <div class="col s12 m9 l9"> + <div class="row"> + <div class= "input-field col s4 l3"> + <select name="englishpos" id="english-pos"> + <option value="default" disabled selected>English pos tagset</option> + <option value="ADD">email</option> + <option value="AFX">affix</option> + <option value="CC">conjunction, coordinating</option> + </select> + <label>Part-of-speech tags</label> + </div> + <div class= "input-field col s4 l3"> + <select name="germanpos" id="german-pos"> + <option value="default" disabled selected>German pos tagset</option> + <option value="ADJA">adjective, attributive</option> + <option value="ADJD">adjective, adverbial or predicative</option> + <option value="ADV">adverb</option> + </select> + </div> + </div> + </div> + </div> + + <div id="simplepos-builder" class="hide"> + <div class="col s12 m9 l9"> + <div class="row"> + <div class= "input-field col s4 l3"> + <select name="simplepos" id="simple-pos"> + <option value="default" disabled selected>simple_pos tagset</option> + <option value="ADJ">adjective</option> + <option value="ADP">adposition</option> + <option value="ADV">adverb</option> + </select> + <label>Simple part-of-speech tags</label> + </div> + </div> + </div> + </div> + + <ul id="incidence-modifiers" class="dropdown-content"> + <li><a id="one-or-more" data-token="+">one or more</a></li> + <li><a id="zero-or-more" data-token="*">zero or more</a></li> + <li><a id="zero-or-one" data-token="?">zero or one</a></li> + <li><a id="exactly-n" class="modal-trigger" href="#exactlyN" data-token="{n}">exactly n</a></li> + <li><a id="between-n-m" class="modal-trigger" href="#betweenNM" data-token="{n,m}" >between n and m</a></li> + </ul> + + </div> + + <div id ="exactlyN" class="modal"> + <div class="row modal-content"> + <div class= "input-field col s10"> + <i class="material-icons prefix">mode_edit</i> + <input placeholder="type in a number for 'n'" type="text" id="n-input"> + </div> + <div class="col s2"> + <p class="btn-floating waves-effect waves-light" id="n-submit"> + <i class="material-icons right">send</i> + </p> + </div> + </div> + </div> + + <div id ="betweenNM" class="modal" style="width:60%;"> + <div class="row modal-content"> + <div class= "input-field col s5"> + <i class="material-icons prefix">mode_edit</i> + <input placeholder="number for 'n'" type="text" id="n-m-input"> + </div> + <div class= "input-field col s5"> + <i class="material-icons prefix">mode_edit</i> + <input placeholder="number for 'm'" type="text" id="m-input"> + </div> + <div class="col s2"> + <p class="btn-floating waves-effect waves-light" id="n-m-submit"> + <i class="material-icons right">send</i> + </p> + </div> + </div> + </div> + + <div id="token-query"></div> + <p></p> + <a class="btn waves-effect disabled waves-light" style="background-color:#00426f" id="build-token-button"> + Add token + <i class="material-icons right">check</i> + </a> + </div> + + <br><hr> + <div> + <h5>Your Query</h5> + <p><i>Examples: </i></p> + <p> + <ent_type="PERSON"> []* </ent_type> []* + [simple_pos="VERB"] :: match.text_publishing_year="1991"; + </p> + <div id="button-preparer"></div> + <div id="your-query"></div> + <p></p> + <a class="btn disabled waves-effect waves-light" id="insert-query-button" style="background-color:#00426f"> + Insert + <i class="material-icons right">send</i> + </a> + </div> + </div> +</div> + +{% endblock modals %} + +{% block scripts %} +{{ super() }} +<script> + + +const concordanceQueryBuilder = new ConcordanceQueryBuilder(word) +</script> +{% endblock scripts %} diff --git a/app/test/__init__.py b/app/test/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..047d8dfccbfa55143c660eeb497250b36c27c6d5 --- /dev/null +++ b/app/test/__init__.py @@ -0,0 +1,5 @@ +from flask import Blueprint + + +bp = Blueprint('test', __name__) +from . import routes diff --git a/app/test/routes.py b/app/test/routes.py new file mode 100644 index 0000000000000000000000000000000000000000..1fe424fa018dd3e6a7dee23a604da85f6bf85782 --- /dev/null +++ b/app/test/routes.py @@ -0,0 +1,10 @@ +from flask import render_template +from flask_login import login_required +from app.models import Corpus, CorpusFile, CorpusStatus +from . import bp +import os + +@bp.route('') +@login_required +def test(): + return render_template('test/analyse_corpus.html.j2', title="Test")