From c3834ca400e772c74bff24de505df55fb05f1f4a Mon Sep 17 00:00:00 2001 From: Inga Kirschnick Date: Tue, 11 Jul 2023 15:52:44 +0200 Subject: [PATCH] Outsource Static Viz to extensions logic --- .../js/CorpusAnalysis/CorpusAnalysisApp.js | 358 +---------------- .../CorpusAnalysisStaticVisualization.js | 364 ++++++++++++++++++ app/templates/_scripts.html.j2 | 1 + .../_analysis/static_visualization.html.j2 | 157 ++++++++ app/templates/corpora/analysis.html.j2 | 147 +------ 5 files changed, 535 insertions(+), 492 deletions(-) create mode 100644 app/static/js/CorpusAnalysis/CorpusAnalysisStaticVisualization.js create mode 100644 app/templates/corpora/_analysis/static_visualization.html.j2 diff --git a/app/static/js/CorpusAnalysis/CorpusAnalysisApp.js b/app/static/js/CorpusAnalysis/CorpusAnalysisApp.js index 7f0462d5..25f58a65 100644 --- a/app/static/js/CorpusAnalysis/CorpusAnalysisApp.js +++ b/app/static/js/CorpusAnalysis/CorpusAnalysisApp.js @@ -1,11 +1,6 @@ class CorpusAnalysisApp { constructor(corpusId) { - this.data = { - stopwords: undefined, - originalStopwords: {}, - stopwordCache: {}, - promises: {getStopwords: undefined} - }; + this.data = {}; // HTML elements this.elements = { @@ -27,24 +22,6 @@ class CorpusAnalysisApp { }; } - getStopwords() { - this.data.promises.getStopwords = new Promise((resolve, reject) => { - Requests.corpora.entity.getStopwords() - .then((response) => { - response.json() - .then((json) => { - this.data.originalStopwords = structuredClone(json); - this.data.stopwords = structuredClone(json); - resolve(this.data.stopwords); - }) - .catch((error) => { - reject(error); - }); - }); - }); - return this.data.promises.getStopwords; - } - init() { this.disableActionElements(); this.elements.m.initModal.open(); @@ -57,11 +34,12 @@ class CorpusAnalysisApp { .then((cqiCorpora) => { this.data.corpus = {o: cqiCorpora[0]}; console.log(this.data.corpus.o.staticData); - this.renderGeneralCorpusInfo(); - this.renderTextInfoList(); - this.renderTextProportionsGraphic() - this.renderFrequenciesGraphic(); - this.renderBoundsGraphic(); + // this.renderGeneralCorpusInfo(); + // this.renderTextInfoList(); + // this.renderTextProportionsGraphic() + // this.renderFrequenciesGraphic(); + // this.renderBoundsGraphic(); + // TODO: Don't do this hgere this.data.corpus.o.updateDb(); this.enableActionElements(); @@ -78,7 +56,6 @@ class CorpusAnalysisApp { progressElement.classList.add('hide'); } ); - // Add event listeners for (let extensionSelectorElement of this.elements.overview.querySelectorAll('.extension-selector')) { @@ -86,25 +63,6 @@ class CorpusAnalysisApp { this.elements.m.extensionTabs.select(extensionSelectorElement.dataset.target); }); } - - let frequenciesStopwordSettingModal = document.querySelector('#frequencies-stopwords-setting-modal'); - let frequenciesStopwordSettingModalButton = document.querySelector('#frequencies-stopwords-setting-modal-button'); - frequenciesStopwordSettingModalButton.addEventListener('click', () => { - this.data.stopwordCache = structuredClone(this.data.stopwords); - this.renderStopwordSettingsModal(this.data.stopwords); - M.Modal.init(frequenciesStopwordSettingModal, {dismissible: false}); - }); - - for (let actionButton of document.querySelectorAll('.frequencies-stopword-setting-modal-action-buttons')) { - actionButton.addEventListener('click', (event) => { - let action = event.target.closest('.frequencies-stopword-setting-modal-action-buttons').dataset.action; - if (action === 'submit') { - this.renderFrequenciesGraphic(); - } else if (action === 'cancel') { - this.data.stopwords = structuredClone(this.data.stopwordCache); - } - }); - } } registerExtension(extension) { @@ -141,306 +99,4 @@ class CorpusAnalysisApp { } } } - - renderGeneralCorpusInfo() { - let corpusData = this.data.corpus.o.staticData; - document.querySelector('.corpus-num-tokens').innerHTML = corpusData.corpus.counts.token; - document.querySelector('.corpus-num-s').innerHTML = corpusData.corpus.counts.s; - document.querySelector('.corpus-num-unique-words').innerHTML = Object.entries(corpusData.corpus.freqs.word).length; - document.querySelector('.corpus-num-unique-lemmas').innerHTML = Object.entries(corpusData.corpus.freqs.lemma).length; - document.querySelector('.corpus-num-unique-pos').innerHTML = Object.entries(corpusData.corpus.freqs.pos).length; - document.querySelector('.corpus-num-unique-simple-pos').innerHTML = Object.entries(corpusData.corpus.freqs.simple_pos).length; - } - - renderTextInfoList() { - let corpusData = this.data.corpus.o.staticData; - let corpusTextInfoListElement = document.querySelector('.corpus-text-info-list'); - let corpusTextInfoList = new CorpusTextInfoList(corpusTextInfoListElement); - let texts = corpusData.s_attrs.text.lexicon; - let textData = []; - for (let i = 0; i < Object.entries(texts).length; i++) { - let resource = { - title: corpusData.values.s_attrs.text[i].title, - publishing_year: corpusData.values.s_attrs.text[i].publishing_year, - num_tokens: corpusData.s_attrs.text.lexicon[i].counts.token, - num_sentences: corpusData.s_attrs.text.lexicon[i].counts.s, - num_unique_words: Object.entries(corpusData.s_attrs.text.lexicon[i].freqs.word).length, - num_unique_lemmas: Object.entries(corpusData.s_attrs.text.lexicon[i].freqs.lemma).length, - num_unique_pos: Object.entries(corpusData.s_attrs.text.lexicon[i].freqs.pos).length, - num_unique_simple_pos: Object.entries(corpusData.s_attrs.text.lexicon[i].freqs.simple_pos).length - }; - - textData.push(resource); - } - - corpusTextInfoList.add(textData); - - let textCountChipElement = document.querySelector('.text-count-chip'); - textCountChipElement.innerHTML = `Text count: ${corpusData.corpus.counts.text}`; - } - - renderTextProportionsGraphic() { - let corpusData = this.data.corpus.o.staticData; - let textProportionsGraphicElement = document.querySelector('#text-proportions-graphic'); - let texts = Object.entries(corpusData.s_attrs.text.lexicon); - let graphData = [ - { - values: texts.map(text => text[1].counts.token), - labels: texts.map(text => `${corpusData.values.s_attrs.text[text[0]].title} (${corpusData.values.s_attrs.text[text[0]].publishing_year})`), - type: 'pie' - } - ]; - let graphLayout = { - showlegend: true, - height: 486, - margin: { - l: 10, - r: 10, - b: 10, - t: 10 - }, - legend: { - "orientation": "h", - font: { - size: 10 - } - } - }; - let config = { - responsive: true, - displaylogo: false - }; - - Plotly.newPlot(textProportionsGraphicElement, graphData, graphLayout, config); - } - - async renderFrequenciesGraphic() { - let corpusData = this.data.corpus.o.staticData; - let frequenciesTokenCategoryDropdownElement = document.querySelector('[data-target="frequencies-token-category-dropdown"]'); - let frequenciesTokenCategoryDropdownListElement = document.querySelector("#frequencies-token-category-dropdown"); - let frequenciesGraphicElement = document.querySelector('#frequencies-graphic'); - let texts = Object.entries(corpusData.s_attrs.text.lexicon); - let graphtype = document.querySelector('.frequencies-graph-mode-button.disabled').dataset.graphType; - let graphModeButtons = document.querySelectorAll('.frequencies-graph-mode-button'); - - frequenciesTokenCategoryDropdownListElement.addEventListener('click', (event) => { - frequenciesTokenCategoryDropdownElement.firstChild.textContent = event.target.innerHTML; - this.renderFrequenciesGraphic(corpusData); - }); - - graphModeButtons.forEach(graphModeButton => { - graphModeButton.addEventListener('click', (event) => { - graphModeButtons.forEach(btn => { - btn.classList.remove('disabled'); - }); - event.target.closest('.frequencies-graph-mode-button').classList.add('disabled'); - this.renderFrequenciesGraphic(corpusData); - }); - }); - - let tokenCategory = frequenciesTokenCategoryDropdownElement.firstChild.textContent.toLowerCase(); - - let graphData = await this.createFrequenciesGraphData(tokenCategory, texts, corpusData, graphtype); - let graphLayout = { - barmode: graphtype === 'bar' ? 'stack' : '', - margin: { - t: 20, - l: 50 - }, - yaxis: { - showticklabels: graphtype === 'markers' ? false : true - }, - }; - let config = { - responsive: true, - modeBarButtonsToRemove: ['zoom2d', 'select2d', 'lasso2d', 'zoomIn2d', 'zoomOut2d', 'autoScale2d', 'resetScale2d'], - displaylogo: false - }; - Plotly.newPlot(frequenciesGraphicElement, graphData, graphLayout, config); - } - - async createFrequenciesGraphData(category, texts, corpusData, graphtype) { - let stopwords = this.data.stopwords; - if (this.data.stopwords === undefined) { - stopwords = await this.getStopwords(); - } - let stopwordList = Object.values(stopwords).flat(); - let graphData = []; - let filteredData = Object.entries(corpusData.corpus.freqs[category]) - .sort((a, b) => b[1] - a[1]) - .filter(item => !stopwordList.includes(corpusData.values.p_attrs[category][item[0]].toLowerCase())) - .slice(0, 5); - - if (graphtype !== 'markers') { - for (let item of filteredData) { - let data = { - x: texts.map(text => `${corpusData.values.s_attrs.text[text[0]].title} (${corpusData.values.s_attrs.text[text[0]].publishing_year})`), - y: texts.map(text => text[1].freqs[category][item[0]] || 0), - name: corpusData.values.p_attrs[category][item[0]], - type: graphtype - }; - graphData.push(data); - } - } else { - for (let item of filteredData) { - let size = texts.map(text => text[1].freqs[category][item[0]] || 0); - let data = { - x: texts.map(text => `${corpusData.values.s_attrs.text[text[0]].title} (${corpusData.values.s_attrs.text[text[0]].publishing_year})`), - y: texts.map(text => corpusData.values.p_attrs[category][item[0]]), - name: corpusData.values.p_attrs[category][item[0]], - text: texts.map(text => `${corpusData.values.p_attrs[category][item[0]]}
${text[1].freqs[category][item[0]] || 0}`), - mode: 'markers', - marker: { - size: size, - sizeref: 0.4 - } - }; - graphData.push(data); - } - } - return graphData; - } - - renderStopwordSettingsModal(stopwords) { - let stopwordInputField = document.querySelector('#stopword-input-field'); - let userStopwordListContainer = document.querySelector('#user-stopword-list-container'); - let stopwordLanguageSelection = document.querySelector('#stopword-language-selection'); - let stopwordLanguageChipList = document.querySelector('#stopword-language-chip-list'); - let deleteLanguageStopwordListEntriesButton = document.querySelector('#delete-language-stopword-list-entries-button'); - let resetLanguageStopwordListEntriesButton = document.querySelector('#reset-language-stopword-list-entries-button'); - - stopwordLanguageChipList.innerHTML = ''; - userStopwordListContainer.innerHTML = ''; - stopwordInputField.value = ''; - - // Render stopword language selection. Set english as default language. Filter out user_stopwords. - if (stopwordLanguageSelection.children.length === 0) { - Object.keys(stopwords).forEach(language => { - if (language !== 'user_stopwords') { - let optionElement = Utils.HTMLToElement(``); - stopwordLanguageSelection.appendChild(optionElement); - } - }); - } - - // Render user stopwords over input field. - if (this.data.stopwords['user_stopwords'].length > 0) { - for (let word of this.data.stopwords['user_stopwords']) { - let chipElement = Utils.HTMLToElement(`
${word}close
`); - chipElement.addEventListener('click', (event) => { - let removedListItem = event.target.closest('.chip').firstChild.textContent; - this.data.stopwords['user_stopwords'] = structuredClone(this.data.stopwords['user_stopwords'].filter(item => item !== removedListItem)); - }); - userStopwordListContainer.appendChild(chipElement); - } - } - - // Render english stopwords as default ... - let selectedLanguage = document.querySelector('#stopword-language-selection').value; - this.renderStopwordLanguageChipList(selectedLanguage, stopwords[selectedLanguage]); - - // ... or render selected language stopwords. - stopwordLanguageSelection.addEventListener('change', (event) => { - this.renderStopwordLanguageChipList(event.target.value, stopwords[event.target.value]); - }); - - // Eventlistener for deleting all stopwords of a language. - deleteLanguageStopwordListEntriesButton.addEventListener('click', (event) => { - let selectedLanguage = stopwordLanguageSelection.value; - this.data.stopwords[selectedLanguage] = []; - stopwordLanguageChipList.innerHTML = ''; - this.buttonRendering(); - }); - - // Eventlistener for resetting all stopwords of a language to the original stopwords. - resetLanguageStopwordListEntriesButton.addEventListener('click', () => { - let selectedLanguage = stopwordLanguageSelection.value; - this.data.stopwords[selectedLanguage] = structuredClone(this.data.originalStopwords[selectedLanguage]); - this.renderStopwordLanguageChipList(selectedLanguage, this.data.stopwords[selectedLanguage]); - }); - - // Initialize Materialize components. - M.Chips.init( - stopwordInputField, - { - placeholder: 'Add stopwords', - onChipAdd: (event) => { - for (let word of event[0].M_Chips.chipsData) { - if (!this.data.stopwords['user_stopwords'].includes(word.tag.toLowerCase())) { - this.data.stopwords['user_stopwords'].push(word.tag.toLowerCase()); - } - } - } - } - ); - M.FormSelect.init(stopwordLanguageSelection); - - } - - buttonRendering() { - let deleteLanguageStopwordListEntriesButton = document.querySelector('#delete-language-stopword-list-entries-button'); - let resetLanguageStopwordListEntriesButton = document.querySelector('#reset-language-stopword-list-entries-button'); - let selectedLanguage = document.querySelector('#stopword-language-selection').value; - let stopwordLength = this.data.stopwords[selectedLanguage].length; - let originalStopwordListLength = this.data.originalStopwords[selectedLanguage].length; - - deleteLanguageStopwordListEntriesButton.classList.toggle('disabled', stopwordLength === 0); - resetLanguageStopwordListEntriesButton.classList.toggle('disabled', stopwordLength === originalStopwordListLength); - } - - renderStopwordLanguageChipList(language, stopwords) { - let stopwordLanguageChipList = document.querySelector('#stopword-language-chip-list'); - stopwordLanguageChipList.innerHTML = ''; - for (let word of stopwords) { - let chipElement = Utils.HTMLToElement(`
${word}close
`); - chipElement.addEventListener('click', (event) => { - let removedListItem = event.target.closest('.chip').firstChild.textContent; - this.data.stopwords[language] = structuredClone(this.data.stopwords[language].filter(item => item !== removedListItem)); - this.buttonRendering(); - }); - stopwordLanguageChipList.appendChild(chipElement); - } - this.buttonRendering(); - } - - renderBoundsGraphic() { - let corpusData = this.data.corpus.o.staticData; - let boundsGraphicElement = document.querySelector('#bounds-graphic'); - - let graphData = []; - let texts = Object.entries(corpusData.s_attrs.text.lexicon); - - graphData = [{ - type: 'bar', - x: texts.map(text => text[1].bounds[1] - text[1].bounds[0]), - y: texts.map(text => corpusData.values.s_attrs.text[text[0]].title), - base: texts.map(text => text[1].bounds[0]), - text: texts.map(text => `${corpusData.values.s_attrs.text[text[0]].title} (${corpusData.values.s_attrs.text[text[0]].publishing_year})`), - orientation: 'h', - hovertemplate: '%{base} - %{x}
%{y}', - showlegend: false - }]; - - let graphLayout = { - barmode: 'stack', - type: 'bar', - showgrid: false, - xaxis: { - rangemode: 'nonnegative', - autorange: true - }, - yaxis: { - autorange: true, - showticklabels: false - } - }; - - let config = { - responsive: true, - modeBarButtonsToRemove: ['zoom2d', 'select2d', 'lasso2d', 'zoomIn2d', 'zoomOut2d', 'autoScale2d', 'resetScale2d'], - displaylogo: false - }; - - Plotly.newPlot(boundsGraphicElement, graphData, graphLayout, config); - } } diff --git a/app/static/js/CorpusAnalysis/CorpusAnalysisStaticVisualization.js b/app/static/js/CorpusAnalysis/CorpusAnalysisStaticVisualization.js new file mode 100644 index 00000000..81e1d466 --- /dev/null +++ b/app/static/js/CorpusAnalysis/CorpusAnalysisStaticVisualization.js @@ -0,0 +1,364 @@ +class CorpusAnalysisStaticVisualization { + name = 'Static Visualization'; + + constructor(app) { + this.app = app; + this.data = { + stopwords: undefined, + originalStopwords: {}, + stopwordCache: {}, + promises: {getStopwords: undefined} + }; + + this.app.registerExtension(this); + } + + init() { + // Init data + this.data.corpus = this.app.data.corpus; + this.renderGeneralCorpusInfo(); + this.renderTextInfoList(); + this.renderTextProportionsGraphic() + this.renderFrequenciesGraphic(); + this.renderBoundsGraphic(); + // Add event listeners + let frequenciesStopwordSettingModal = document.querySelector('#frequencies-stopwords-setting-modal'); + let frequenciesStopwordSettingModalButton = document.querySelector('#frequencies-stopwords-setting-modal-button'); + frequenciesStopwordSettingModalButton.addEventListener('click', () => { + this.data.stopwordCache = structuredClone(this.data.stopwords); + this.renderStopwordSettingsModal(this.data.stopwords); + M.Modal.init(frequenciesStopwordSettingModal, {dismissible: false}); + }); + + for (let actionButton of document.querySelectorAll('.frequencies-stopword-setting-modal-action-buttons')) { + actionButton.addEventListener('click', (event) => { + let action = event.target.closest('.frequencies-stopword-setting-modal-action-buttons').dataset.action; + if (action === 'submit') { + this.renderFrequenciesGraphic(); + } else if (action === 'cancel') { + this.data.stopwords = structuredClone(this.data.stopwordCache); + } + }); + } + } + + getStopwords() { + this.data.promises.getStopwords = new Promise((resolve, reject) => { + Requests.corpora.entity.getStopwords() + .then((response) => { + response.json() + .then((json) => { + this.data.originalStopwords = structuredClone(json); + this.data.stopwords = structuredClone(json); + resolve(this.data.stopwords); + }) + .catch((error) => { + reject(error); + }); + }); + }); + return this.data.promises.getStopwords; + } + + renderGeneralCorpusInfo() { + let corpusData = this.data.corpus.o.staticData; + document.querySelector('.corpus-num-tokens').innerHTML = corpusData.corpus.counts.token; + document.querySelector('.corpus-num-s').innerHTML = corpusData.corpus.counts.s; + document.querySelector('.corpus-num-unique-words').innerHTML = Object.entries(corpusData.corpus.freqs.word).length; + document.querySelector('.corpus-num-unique-lemmas').innerHTML = Object.entries(corpusData.corpus.freqs.lemma).length; + document.querySelector('.corpus-num-unique-pos').innerHTML = Object.entries(corpusData.corpus.freqs.pos).length; + document.querySelector('.corpus-num-unique-simple-pos').innerHTML = Object.entries(corpusData.corpus.freqs.simple_pos).length; + } + + renderTextInfoList() { + let corpusData = this.data.corpus.o.staticData; + let corpusTextInfoListElement = document.querySelector('.corpus-text-info-list'); + let corpusTextInfoList = new CorpusTextInfoList(corpusTextInfoListElement); + let texts = corpusData.s_attrs.text.lexicon; + let textData = []; + for (let i = 0; i < Object.entries(texts).length; i++) { + let resource = { + title: corpusData.values.s_attrs.text[i].title, + publishing_year: corpusData.values.s_attrs.text[i].publishing_year, + num_tokens: corpusData.s_attrs.text.lexicon[i].counts.token, + num_sentences: corpusData.s_attrs.text.lexicon[i].counts.s, + num_unique_words: Object.entries(corpusData.s_attrs.text.lexicon[i].freqs.word).length, + num_unique_lemmas: Object.entries(corpusData.s_attrs.text.lexicon[i].freqs.lemma).length, + num_unique_pos: Object.entries(corpusData.s_attrs.text.lexicon[i].freqs.pos).length, + num_unique_simple_pos: Object.entries(corpusData.s_attrs.text.lexicon[i].freqs.simple_pos).length + }; + + textData.push(resource); + } + + corpusTextInfoList.add(textData); + + let textCountChipElement = document.querySelector('.text-count-chip'); + textCountChipElement.innerHTML = `Text count: ${corpusData.corpus.counts.text}`; + } + + renderTextProportionsGraphic() { + let corpusData = this.data.corpus.o.staticData; + let textProportionsGraphicElement = document.querySelector('#text-proportions-graphic'); + let texts = Object.entries(corpusData.s_attrs.text.lexicon); + let graphData = [ + { + values: texts.map(text => text[1].counts.token), + labels: texts.map(text => `${corpusData.values.s_attrs.text[text[0]].title} (${corpusData.values.s_attrs.text[text[0]].publishing_year})`), + type: 'pie' + } + ]; + let graphLayout = { + showlegend: true, + height: 486, + margin: { + l: 10, + r: 10, + b: 10, + t: 10 + }, + legend: { + "orientation": "h", + font: { + size: 10 + } + } + }; + let config = { + responsive: true, + displaylogo: false + }; + + Plotly.newPlot(textProportionsGraphicElement, graphData, graphLayout, config); + } + + async renderFrequenciesGraphic() { + let corpusData = this.data.corpus.o.staticData; + let frequenciesTokenCategoryDropdownElement = document.querySelector('[data-target="frequencies-token-category-dropdown"]'); + let frequenciesTokenCategoryDropdownListElement = document.querySelector("#frequencies-token-category-dropdown"); + let frequenciesGraphicElement = document.querySelector('#frequencies-graphic'); + let texts = Object.entries(corpusData.s_attrs.text.lexicon); + let graphtype = document.querySelector('.frequencies-graph-mode-button.disabled').dataset.graphType; + let graphModeButtons = document.querySelectorAll('.frequencies-graph-mode-button'); + + frequenciesTokenCategoryDropdownListElement.addEventListener('click', (event) => { + frequenciesTokenCategoryDropdownElement.firstChild.textContent = event.target.innerHTML; + this.renderFrequenciesGraphic(corpusData); + }); + + graphModeButtons.forEach(graphModeButton => { + graphModeButton.addEventListener('click', (event) => { + graphModeButtons.forEach(btn => { + btn.classList.remove('disabled'); + }); + event.target.closest('.frequencies-graph-mode-button').classList.add('disabled'); + this.renderFrequenciesGraphic(corpusData); + }); + }); + + let tokenCategory = frequenciesTokenCategoryDropdownElement.firstChild.textContent.toLowerCase(); + + let graphData = await this.createFrequenciesGraphData(tokenCategory, texts, corpusData, graphtype); + let graphLayout = { + barmode: graphtype === 'bar' ? 'stack' : '', + margin: { + t: 20, + l: 50 + }, + yaxis: { + showticklabels: graphtype === 'markers' ? false : true + }, + }; + let config = { + responsive: true, + modeBarButtonsToRemove: ['zoom2d', 'select2d', 'lasso2d', 'zoomIn2d', 'zoomOut2d', 'autoScale2d', 'resetScale2d'], + displaylogo: false + }; + Plotly.newPlot(frequenciesGraphicElement, graphData, graphLayout, config); + } + + async createFrequenciesGraphData(category, texts, corpusData, graphtype) { + let stopwords = this.data.stopwords; + if (this.data.stopwords === undefined) { + stopwords = await this.getStopwords(); + } + let stopwordList = Object.values(stopwords).flat(); + let graphData = []; + let filteredData = Object.entries(corpusData.corpus.freqs[category]) + .sort((a, b) => b[1] - a[1]) + .filter(item => !stopwordList.includes(corpusData.values.p_attrs[category][item[0]].toLowerCase())) + .slice(0, 5); + + if (graphtype !== 'markers') { + for (let item of filteredData) { + let data = { + x: texts.map(text => `${corpusData.values.s_attrs.text[text[0]].title} (${corpusData.values.s_attrs.text[text[0]].publishing_year})`), + y: texts.map(text => text[1].freqs[category][item[0]] || 0), + name: corpusData.values.p_attrs[category][item[0]], + type: graphtype + }; + graphData.push(data); + } + } else { + for (let item of filteredData) { + let size = texts.map(text => text[1].freqs[category][item[0]] || 0); + let data = { + x: texts.map(text => `${corpusData.values.s_attrs.text[text[0]].title} (${corpusData.values.s_attrs.text[text[0]].publishing_year})`), + y: texts.map(text => corpusData.values.p_attrs[category][item[0]]), + name: corpusData.values.p_attrs[category][item[0]], + text: texts.map(text => `${corpusData.values.p_attrs[category][item[0]]}
${text[1].freqs[category][item[0]] || 0}`), + mode: 'markers', + marker: { + size: size, + sizeref: 0.4 + } + }; + graphData.push(data); + } + } + return graphData; + } + + renderStopwordSettingsModal(stopwords) { + let stopwordInputField = document.querySelector('#stopword-input-field'); + let userStopwordListContainer = document.querySelector('#user-stopword-list-container'); + let stopwordLanguageSelection = document.querySelector('#stopword-language-selection'); + let stopwordLanguageChipList = document.querySelector('#stopword-language-chip-list'); + let deleteLanguageStopwordListEntriesButton = document.querySelector('#delete-language-stopword-list-entries-button'); + let resetLanguageStopwordListEntriesButton = document.querySelector('#reset-language-stopword-list-entries-button'); + + stopwordLanguageChipList.innerHTML = ''; + userStopwordListContainer.innerHTML = ''; + stopwordInputField.value = ''; + + // Render stopword language selection. Set english as default language. Filter out user_stopwords. + if (stopwordLanguageSelection.children.length === 0) { + Object.keys(stopwords).forEach(language => { + if (language !== 'user_stopwords') { + let optionElement = Utils.HTMLToElement(``); + stopwordLanguageSelection.appendChild(optionElement); + } + }); + } + + // Render user stopwords over input field. + if (this.data.stopwords['user_stopwords'].length > 0) { + for (let word of this.data.stopwords['user_stopwords']) { + let chipElement = Utils.HTMLToElement(`
${word}close
`); + chipElement.addEventListener('click', (event) => { + let removedListItem = event.target.closest('.chip').firstChild.textContent; + this.data.stopwords['user_stopwords'] = structuredClone(this.data.stopwords['user_stopwords'].filter(item => item !== removedListItem)); + }); + userStopwordListContainer.appendChild(chipElement); + } + } + + // Render english stopwords as default ... + let selectedLanguage = document.querySelector('#stopword-language-selection').value; + this.renderStopwordLanguageChipList(selectedLanguage, stopwords[selectedLanguage]); + + // ... or render selected language stopwords. + stopwordLanguageSelection.addEventListener('change', (event) => { + this.renderStopwordLanguageChipList(event.target.value, stopwords[event.target.value]); + }); + + // Eventlistener for deleting all stopwords of a language. + deleteLanguageStopwordListEntriesButton.addEventListener('click', (event) => { + let selectedLanguage = stopwordLanguageSelection.value; + this.data.stopwords[selectedLanguage] = []; + stopwordLanguageChipList.innerHTML = ''; + this.buttonRendering(); + }); + + // Eventlistener for resetting all stopwords of a language to the original stopwords. + resetLanguageStopwordListEntriesButton.addEventListener('click', () => { + let selectedLanguage = stopwordLanguageSelection.value; + this.data.stopwords[selectedLanguage] = structuredClone(this.data.originalStopwords[selectedLanguage]); + this.renderStopwordLanguageChipList(selectedLanguage, this.data.stopwords[selectedLanguage]); + }); + + // Initialize Materialize components. + M.Chips.init( + stopwordInputField, + { + placeholder: 'Add stopwords', + onChipAdd: (event) => { + for (let word of event[0].M_Chips.chipsData) { + if (!this.data.stopwords['user_stopwords'].includes(word.tag.toLowerCase())) { + this.data.stopwords['user_stopwords'].push(word.tag.toLowerCase()); + } + } + } + } + ); + M.FormSelect.init(stopwordLanguageSelection); + + } + + buttonRendering() { + let deleteLanguageStopwordListEntriesButton = document.querySelector('#delete-language-stopword-list-entries-button'); + let resetLanguageStopwordListEntriesButton = document.querySelector('#reset-language-stopword-list-entries-button'); + let selectedLanguage = document.querySelector('#stopword-language-selection').value; + let stopwordLength = this.data.stopwords[selectedLanguage].length; + let originalStopwordListLength = this.data.originalStopwords[selectedLanguage].length; + + deleteLanguageStopwordListEntriesButton.classList.toggle('disabled', stopwordLength === 0); + resetLanguageStopwordListEntriesButton.classList.toggle('disabled', stopwordLength === originalStopwordListLength); + } + + renderStopwordLanguageChipList(language, stopwords) { + let stopwordLanguageChipList = document.querySelector('#stopword-language-chip-list'); + stopwordLanguageChipList.innerHTML = ''; + for (let word of stopwords) { + let chipElement = Utils.HTMLToElement(`
${word}close
`); + chipElement.addEventListener('click', (event) => { + let removedListItem = event.target.closest('.chip').firstChild.textContent; + this.data.stopwords[language] = structuredClone(this.data.stopwords[language].filter(item => item !== removedListItem)); + this.buttonRendering(); + }); + stopwordLanguageChipList.appendChild(chipElement); + } + this.buttonRendering(); + } + + renderBoundsGraphic() { + let corpusData = this.data.corpus.o.staticData; + let boundsGraphicElement = document.querySelector('#bounds-graphic'); + + let graphData = []; + let texts = Object.entries(corpusData.s_attrs.text.lexicon); + + graphData = [{ + type: 'bar', + x: texts.map(text => text[1].bounds[1] - text[1].bounds[0]), + y: texts.map(text => corpusData.values.s_attrs.text[text[0]].title), + base: texts.map(text => text[1].bounds[0]), + text: texts.map(text => `${corpusData.values.s_attrs.text[text[0]].title} (${corpusData.values.s_attrs.text[text[0]].publishing_year})`), + orientation: 'h', + hovertemplate: '%{base} - %{x}
%{y}', + showlegend: false + }]; + + let graphLayout = { + barmode: 'stack', + type: 'bar', + showgrid: false, + xaxis: { + rangemode: 'nonnegative', + autorange: true + }, + yaxis: { + autorange: true, + showticklabels: false + } + }; + + let config = { + responsive: true, + modeBarButtonsToRemove: ['zoom2d', 'select2d', 'lasso2d', 'zoomIn2d', 'zoomOut2d', 'autoScale2d', 'resetScale2d'], + displaylogo: false + }; + + Plotly.newPlot(boundsGraphicElement, graphData, graphLayout, config); + } +} diff --git a/app/templates/_scripts.html.j2 b/app/templates/_scripts.html.j2 index d8dfa6d1..772a0596 100644 --- a/app/templates/_scripts.html.j2 +++ b/app/templates/_scripts.html.j2 @@ -29,6 +29,7 @@ 'js/CorpusAnalysis/CorpusAnalysisApp.js', 'js/CorpusAnalysis/CorpusAnalysisConcordance.js', 'js/CorpusAnalysis/CorpusAnalysisReader.js', + 'js/CorpusAnalysis/CorpusAnalysisStaticVisualization.js', 'js/CorpusAnalysis/QueryBuilder.js', 'js/XMLtoObject.js' %} diff --git a/app/templates/corpora/_analysis/static_visualization.html.j2 b/app/templates/corpora/_analysis/static_visualization.html.j2 new file mode 100644 index 00000000..3e6cfcff --- /dev/null +++ b/app/templates/corpora/_analysis/static_visualization.html.j2 @@ -0,0 +1,157 @@ +{% set name = 'Static Visualization' %} + +{% set description = '' %} + +{% set id_prefix = name.lower().replace(' ', '-') + '-extension' %} + +{% set tab_content = '' %} + +{% set container_content %} +
+
+

query_stats{{ name }}

+
+
+
+
+
+
+

Tokens

+ +
+
+
+
+
+
+

Sentences

+ +
+
+
+
+
+
+

Unique words

+ +
+
+
+
+
+
+

Unique lemmas

+ +
+
+
+
+
+
+

Unique pos

+ +
+
+
+
+
+
+

Unique simple_pos

+ +
+
+
+
+
+
+
+
+ Text Information Overview +
+
+
+
+
+
+
+
+
+
+ Proportions +

of texts within the corpus

+
+
+
+
+
+
+
+ Frequencies + +

within the texts of the 5 most frequent words in the corpus

+
+ Wordarrow_drop_down + equalizer + show_chart + bubble_chart + settings +
+
+
+
+ +
+
+
+
+ Text Bounds +
+
+
+
+
+{% endset %} + +{% set modals %} + @@ -165,38 +62,6 @@ -