diff --git a/app/corpora/cqi_over_socketio/__init__.py b/app/corpora/cqi_over_socketio/__init__.py index d914973c88b906c592fb9f140ed34c5620b69dfe..68f28d8f0bc14950dc5203d4e6a1838d18260a27 100644 --- a/app/corpora/cqi_over_socketio/__init__.py +++ b/app/corpora/cqi_over_socketio/__init__.py @@ -1,4 +1,4 @@ -from app import db, socketio +from app import db, hashids, socketio from app.decorators import socketio_login_required from app.models import Corpus from flask import session @@ -57,7 +57,7 @@ from .cqi import * # noqa def connect(auth): # the auth variable is used in a hacky way. It contains the corpus id for # which a corpus analysis session should be started. - corpus_id = auth['corpus_id'] + corpus_id = hashids.decode(auth['corpus_id'])[0] corpus = Corpus.query.get(corpus_id) if corpus is None: # return {'code': 404, 'msg': 'Not Found'} diff --git a/app/events/sqlalchemy.py b/app/events/sqlalchemy.py index 457a2bde8b245ead1b4c324e06359189025256a9..b3362ca00d3e271864fb23902c33f6e3dbc4e00a 100644 --- a/app/events/sqlalchemy.py +++ b/app/events/sqlalchemy.py @@ -28,6 +28,11 @@ def ressource_after_delete(mapper, connection, ressource): @db.event.listens_for(QueryResult, 'after_insert') def ressource_after_insert_handler(mapper, connection, ressource): value = ressource.to_dict(backrefs=False, relationships=False) + if isinstance(ressource, Job): + value['inputs'] = {} + value['results'] = {} + elif isinstance(ressource, Corpus): + value['files'] = {} jsonpatch = [ {'op': 'add', 'path': ressource.jsonpatch_path, 'value': value} ] diff --git a/app/models.py b/app/models.py index 23b91cf31ab7620c503419734be89c61eeac05cc..1572a14486b55d4bbc6c51cf911ba56def9f2940 100644 --- a/app/models.py +++ b/app/models.py @@ -544,7 +544,7 @@ class CorpusFile(FileMixin, HashidMixin, db.Model): @property def jsonpatch_path(self): - return f'/{self.corpus.jsonpatch_path}/files/{self.hashid}' + return f'{self.corpus.jsonpatch_path}/files/{self.hashid}' @property def path(self): @@ -601,6 +601,7 @@ class CorpusFile(FileMixin, HashidMixin, db.Model): if backrefs: dict_corpus_file['corpus'] = self.corpus.to_dict( backrefs=True, relationships=False) + return dict_corpus_file class Corpus(HashidMixin, db.Model): @@ -705,7 +706,7 @@ class Corpus(HashidMixin, db.Model): backrefs=True, relationships=False) if relationships: dict_corpus['files'] = { - x.id: x.to_dict(backrefs=False, relationships=True) + x.hashid: x.to_dict(backrefs=False, relationships=True) for x in self.files } return dict_corpus diff --git a/app/static/js/nopaque/App.js b/app/static/js/nopaque/App.js index 6bad75e8a00dee1bed76278da49f4ce8c7dfd9ff..aff90f3a5f25837a27d48c6044f47271a4f7767f 100644 --- a/app/static/js/nopaque/App.js +++ b/app/static/js/nopaque/App.js @@ -65,33 +65,9 @@ class App { } usersPatchHandler(patch) { - let jobId; let listener; - let match; - let operation; - let re; - let relationship; - let ressourceId; - let userId; - for (operation of patch.filter(operation => operation.op === 'add')) { - re = new RegExp(`^/users/([A-Za-z0-9]*)/corpora/([A-Za-z0-9]*)/(files)`); - if (re.test(operation.path)) { - [match, userId, ressourceId, relationship] = operation.path.match(re); - if (!(relationship in this.users[userId].corpora[ressourceId])) { - this.users[userId].corpora[ressourceId][relationship] = {}; - } - continue; - } - re = new RegExp(`^/users/([A-Za-z0-9]*)/jobs/([A-Za-z0-9]*)/(inputs|results)`); - if (re.test(operation.path)) { - [match, userId, ressourceId, relationship] = operation.path.match(re); - if (!(relationship in this.users[userId].jobs[ressourceId])) { - this.users[userId].jobs[ressourceId][relationship] = {}; - } - continue; - } - } + console.log(patch); this.data = jsonpatch.apply_patch(this.data, patch); for (listener of this.eventListeners['users.patch']) {listener(patch);} } diff --git a/app/static/js/nopaque/CorpusAnalysis/CQiClient.js b/app/static/js/nopaque/CorpusAnalysis/CQiClient.js index 42efef511f71a40d620be5463908c82227e56f4e..a605eb076d3ac0e3e6b6a430f60fb34ddb9cb52b 100644 --- a/app/static/js/nopaque/CorpusAnalysis/CQiClient.js +++ b/app/static/js/nopaque/CorpusAnalysis/CQiClient.js @@ -2,7 +2,11 @@ class CQiClient { constructor(corpusId) { this.socket = io( '/corpora/corpus/corpus_analysis', - {auth: {corpus_id: corpusId}, transports: ['websocket'], upgrade: false} + { + auth: {corpus_id: corpusId}, + transports: ['websocket'], + upgrade: false + } ); this.connected = false; this.corpora = new CQiCorpusCollection(this.socket); @@ -55,7 +59,8 @@ class CQiCorpusCollection { get(corpusName) { return new Promise((resolve, reject) => { - let args = {corpus_name: corpusName}; + const args = {corpus_name: corpusName}; + this.socket.emit('cqi.corpora.get', args, response => { if (response.code === 200) { resolve(new CQiCorpus(this.socket, response.payload)); @@ -95,7 +100,8 @@ class CQiCorpus { drop() { return new Promise((resolve, reject) => { - let args = {corpus_name: this.name}; + const args = {corpus_name: this.name}; + this.socket.emit('cqi.corpora.corpus.drop', args, response => { if (response.code === 200) { resolve(response.payload); @@ -108,11 +114,12 @@ class CQiCorpus { query(subcorpus_name, queryString) { return new Promise((resolve, reject) => { - let args = { + const args = { corpus_name: this.name, subcorpus_name: subcorpus_name, query: queryString }; + this.socket.emit('cqi.corpora.corpus.query', args, response => { if (response.code === 200) { resolve(response.payload); @@ -126,7 +133,8 @@ class CQiCorpus { // nopaque specific CQi extension paginate(page=1, perPage=20) { return new Promise((resolve, reject) => { - let args = {corpus_name: this.name, page: page, per_page: perPage}; + const args = {corpus_name: this.name, page: page, per_page: perPage}; + this.socket.emit('cqi.corpora.corpus.paginate', args, response => { if (response.code === 200) { resolve(response.payload); @@ -138,7 +146,8 @@ class CQiCorpus { } updateDb() { - let args = {corpus_name: this.name}; + const args = {corpus_name: this.name}; + this.socket.emit('cqi.corpora.corpus.update_db', args); } } @@ -152,8 +161,11 @@ class CQiAlignmentAttributeCollection { get(alignmentAttributeName) { return new Promise((resolve, reject) => { - let args = {corpus_name: this.corpus.name, - alignment_attribute_name: alignmentAttributeName}; + const args = { + corpus_name: this.corpus.name, + alignment_attribute_name: alignmentAttributeName + }; + this.socket.emit('cqi.corpora.corpus.alignment_attributes.get', args, response => { if (response.code === 200) { resolve(new CQiAlignmentAttribute(this.socket, this.corpus, response.payload)); @@ -166,7 +178,8 @@ class CQiAlignmentAttributeCollection { list() { return new Promise((resolve, reject) => { - let args = {corpus_name: this.corpus.name}; + const args = {corpus_name: this.corpus.name}; + this.socket.emit('cqi.corpus.alignment_attributes.list', args, response => { if (response.code === 200) { resolve(response.payload.map(x => {return new CQiAlignmentAttribute(this.socket, this.corpus, x);})); @@ -197,10 +210,11 @@ class CQiPositionalAttributeCollection { get(positionalAttributeName) { return new Promise((resolve, reject) => { - let args = { + const args = { corpus_name: this.corpus.name, positional_attribute_name: positionalAttributeName }; + this.socket.emit('cqi.corpora.corpus.positional_attributes.get', args, response => { if (response.code === 200) { resolve(new CQiPositionalAttribute(this.socket, this.corpus, response.payload)); @@ -213,7 +227,8 @@ class CQiPositionalAttributeCollection { list() { return new Promise((resolve, reject) => { - let args = {corpus_name: this.corpus.name}; + const args = {corpus_name: this.corpus.name}; + this.socket.emit('cqi.corpus.positional_attributes.list', args, response => { if (response.code === 200) { resolve(response.payload.map(x => {return new CQiPositionalAttribute(this.socket, this.corpus, x);})); @@ -245,10 +260,11 @@ class CQiStructuralAttributeCollection { get(structuralAttributeName) { return new Promise((resolve, reject) => { - let args = { + const args = { corpus_name: this.corpus.name, structural_attribute_name: structuralAttributeName }; + this.socket.emit('cqi.corpora.corpus.structural_attributes.get', args, response => { if (response.code === 200) { resolve(new CQiStructuralAttribute(this.socket, this.corpus, response.payload)); @@ -261,7 +277,8 @@ class CQiStructuralAttributeCollection { list() { return new Promise((resolve, reject) => { - let args = {corpus_name: this.corpus.name}; + const args = {corpus_name: this.corpus.name}; + this.socket.emit('cqi.corpus.structural_attributes.list', args, response => { if (response.code === 200) { resolve(response.payload.map(x => {return new CQiStructuralAttribute(this.socket, this.corpus, x);})); @@ -293,7 +310,10 @@ class CQiSubcorpusCollection { get(subcorpusName) { return new Promise((resolve, reject) => { - let args = {corpus_name: this.corpus.name, subcorpus_name: subcorpusName}; + const args = { + corpus_name: this.corpus.name, + subcorpus_name: subcorpusName + }; this.socket.emit('cqi.corpora.corpus.subcorpora.get', args, response => { if (response.code === 200) { resolve(new CQiSubcorpus(this.socket, this.corpus, response.payload)); @@ -306,7 +326,8 @@ class CQiSubcorpusCollection { list() { return new Promise((resolve, reject) => { - let args = {corpus_name: this.corpus.name}; + const args = {corpus_name: this.corpus.name}; + this.socket.emit('cqi.corpora.corpus.subcorpora.list', args, response => { if (response.code === 200) { resolve(response.payload.map(x => {return new CQiSubcorpus(this.socket, this.corpus, x);})); @@ -330,7 +351,8 @@ class CQiSubcorpus { drop() { return new Promise((resolve, reject) => { - let args = {corpus_name: this.corpus.name, subcorpus_name: this.name}; + const args = {corpus_name: this.corpus.name, subcorpus_name: this.name}; + this.socket.emit('cqi.corpora.corpus.subcorpora.subcorpus.drop', args, response => { if (response.code === 200) { resolve(response.payload); @@ -343,13 +365,14 @@ class CQiSubcorpus { dump(field, first, last) { return new Promise((resolve, reject) => { - let args = { + const args = { corpus_name: this.corpus.name, subcorpus_name: this.name, field: field, first: first, last: last }; + this.socket.emit('cqi.corpora.corpus.subcorpora.subcorpus.dump', args, response => { if (response.code === 200) { resolve(response.payload); @@ -362,11 +385,12 @@ class CQiSubcorpus { export(context=50) { return new Promise((resolve, reject) => { - let args = { + const args = { corpus_name: this.corpus.name, subcorpus_name: this.name, context: context }; + this.socket.emit('cqi.corpora.corpus.subcorpora.subcorpus.export', args, response => { if (response.code === 200) { resolve(response.payload); @@ -379,13 +403,14 @@ class CQiSubcorpus { fdst_1(cutoff, field, attribute) { return new Promise((resolve, reject) => { - let args = { + const args = { corpus_name: this.corpus.name, subcorpus_name: this.name, cutoff: cutoff, field: field, attribute: attribute }; + this.socket.emit('cqi.corpora.corpus.subcorpora.subcorpus.fdist_1', args, response => { if (response.code === 200) { resolve(response.payload); @@ -398,7 +423,7 @@ class CQiSubcorpus { fdst_2(cutoff, field1, attribute1, field2, attribute2) { return new Promise((resolve, reject) => { - let args = { + const args = { corpus_name: this.corpus.name, subcorpus_name: this.name, cutoff: cutoff, @@ -407,6 +432,7 @@ class CQiSubcorpus { field2: field2, attribute2: attribute2 }; + this.socket.emit('cqi.corpora.corpus.subcorpora.subcorpus.fdist_1', args, response => { if (response.code === 200) { resolve(response.payload); @@ -420,13 +446,14 @@ class CQiSubcorpus { // nopaque specific CQi extension paginate(page=1, perPage=20, context=50) { return new Promise((resolve, reject) => { - let args = { + const args = { corpus_name: this.corpus.name, subcorpus_name: this.name, page: page, per_page: perPage, context: context }; + this.socket.emit('cqi.corpora.corpus.subcorpora.subcorpus.paginate', args, response => { if (response.code === 200) { resolve(response.payload); diff --git a/app/static/js/nopaque/CorpusAnalysis/CorpusAnalysisApp.js b/app/static/js/nopaque/CorpusAnalysis/CorpusAnalysisApp.js index 71a4780a62a828ffbdf5cf3e201aace82593ae97..ad324e34d2c8db2fbf59d7514b3672ba67cde5f6 100644 --- a/app/static/js/nopaque/CorpusAnalysis/CorpusAnalysisApp.js +++ b/app/static/js/nopaque/CorpusAnalysis/CorpusAnalysisApp.js @@ -69,7 +69,7 @@ class CorpusAnalysisApp { this.elements.initError.classList.remove('hide'); this.elements.initProgress.classList.add('hide'); if ('payload' in cQiError && 'code' in cQiError.payload && 'msg' in cQiError.payload) { - nopaque.appClient.flash(`${cQiError.payload.code}: ${cQiError.payload.msg}`, 'error'); + app.flash(`${cQiError.payload.code}: ${cQiError.payload.msg}`, 'error'); } } ); diff --git a/app/static/js/nopaque/CorpusAnalysis/CorpusAnalysisConcordance.js b/app/static/js/nopaque/CorpusAnalysis/CorpusAnalysisConcordance.js index 24a9ab532a3bbfc11fc6a32a2d3198a839cd9abe..b6612e010c6b175a28fe53e8c1aa154e93d5be52 100644 --- a/app/static/js/nopaque/CorpusAnalysis/CorpusAnalysisConcordance.js +++ b/app/static/js/nopaque/CorpusAnalysis/CorpusAnalysisConcordance.js @@ -71,7 +71,7 @@ class CorpusAnalysisConcordance { this.elements.error.innerText = JSON.stringify(cQiError); this.elements.error.classList.remove('hide'); if ('payload' in cQiError && 'code' in cQiError.payload && 'msg' in cQiError.payload) { - nopaque.appClient.flash(`${cQiError.payload.code}: ${cQiError.payload.msg}`, 'error'); + app.flash(`${cQiError.payload.code}: ${cQiError.payload.msg}`, 'error'); } this.elements.progress.classList.add('hide'); this.app.enableActionElements(); @@ -166,7 +166,7 @@ class CorpusAnalysisConcordance { let subcorpus = this.data.subcorpora[this.settings.selectedSubcorpus]; subcorpus.o.drop().then( cQiStatus => { - nopaque.appClient.flash(`${subcorpus.o.name} deleted`, 'corpus'); + app.flash(`${subcorpus.o.name} deleted`, 'corpus'); delete this.data.subcorpora[subcorpus.o.name]; this.settings.selectedSubcorpus = undefined; for (let subcorpusName in this.data.subcorpora) { @@ -187,7 +187,7 @@ class CorpusAnalysisConcordance { } }, cQiError => { - nopaque.appClient.flash(`${cQiError.payload.code}: ${cQiError.payload.msg}`, 'error'); + app.flash(`${cQiError.payload.code}: ${cQiError.payload.msg}`, 'error'); } ); }); diff --git a/app/static/js/nopaque/CorpusAnalysis/CorpusAnalysisReader.js b/app/static/js/nopaque/CorpusAnalysis/CorpusAnalysisReader.js index 2b2a584337abde08accebcdb09d19ff715c3566b..649029883dbd48c16420d757688b2aab777a2358 100644 --- a/app/static/js/nopaque/CorpusAnalysis/CorpusAnalysisReader.js +++ b/app/static/js/nopaque/CorpusAnalysis/CorpusAnalysisReader.js @@ -49,7 +49,7 @@ class CorpusAnalysisReader { this.elements.error.innerText = JSON.stringify(error); this.elements.error.classList.remove('hide'); if ('payload' in error && 'code' in error.payload && 'msg' in error.payload) { - nopaque.appClient.flash(`${error.payload.code}: ${error.payload.msg}`, 'error'); + app.flash(`${error.payload.code}: ${error.payload.msg}`, 'error'); } this.elements.progress.classList.add('hide'); this.app.enableActionElements(); @@ -75,8 +75,12 @@ class CorpusAnalysisReader { } clearCorpus() { + let pAttrElement; + let pAttrElements; + // Destroy with .p-attr elements associated Materialize tooltips - for (let pAttrElement of this.elements.corpus.querySelectorAll('.p-attr.tooltipped')) { + pAttrElements = this.elements.corpus.querySelectorAll('.p-attr.tooltipped'); + for (pAttrElement of pAttrElements) { M.Tooltip.getInstance(pAttrElement)?.destroy(); } this.elements.corpus.innerHTML = ` @@ -88,8 +92,10 @@ class CorpusAnalysisReader { } renderCorpus() { + let item; + this.clearCorpus(); - let item = this.data.corpus.p.items[0]; + item = this.data.corpus.p.items[0]; this.elements.corpus.innerHTML += ` <p>${this.cposRange2HTML(item[0], item[item.length - 1])}</p> `.trim(); @@ -103,6 +109,10 @@ class CorpusAnalysisReader { } renderCorpusPagination() { + let i; + let page; + let paginateTriggerElement; + this.clearCorpusPagination(); if (this.data.corpus.p.pages === 0) {return;} this.elements.corpusPagination.innerHTML += ` @@ -119,7 +129,7 @@ class CorpusAnalysisReader { </a> </li> `.trim(); - for (let i = 1; i <= this.data.corpus.p.pages; i++) { + for (i = 1; i <= this.data.corpus.p.pages; i++) { this.elements.corpusPagination.innerHTML += ` <li class="${i === this.data.corpus.p.page ? 'active' : 'waves-effect'}"> <a class="corpus-analysis-action pagination-trigger" ${i === this.data.corpus.p.page ? '' : 'data-target="' + i + '"'}>${i}</a> @@ -140,10 +150,10 @@ class CorpusAnalysisReader { </a> </li> `.trim(); - for (let paginateTriggerElement of this.elements.corpusPagination.querySelectorAll('.pagination-trigger[data-target]')) { + for (paginateTriggerElement of this.elements.corpusPagination.querySelectorAll('.pagination-trigger[data-target]')) { paginateTriggerElement.addEventListener('click', event => { event.preventDefault(); - let page = parseInt(paginateTriggerElement.dataset.target); + page = parseInt(paginateTriggerElement.dataset.target); this.page(page); }); } @@ -151,10 +161,11 @@ class CorpusAnalysisReader { } cposRange2HTML(firstCpos, lastCpos) { + let cpos; let prevPAttr, pAttr, nextPAttr; let isEntityStart, isEntityEnd; let html = ''; - for (let cpos = firstCpos; cpos <= lastCpos; cpos++) { + for (cpos = firstCpos; cpos <= lastCpos; cpos++) { prevPAttr = cpos > firstCpos ? this.data.corpus.p.lookups.cpos_lookup[cpos - 1] : null; pAttr = this.data.corpus.p.lookups.cpos_lookup[cpos]; nextPAttr = cpos < lastCpos ? this.data.corpus.p.lookups.cpos_lookup[cpos + 1] : null; diff --git a/app/static/js/nopaque/RessourceLists/CorpusFileList.js b/app/static/js/nopaque/RessourceLists/CorpusFileList.js index e49ee8fa1bfc3cc3cf478a3081c9236569a11956..ed6a4b493bd3fd3e39ea146b7471bb1a1f9ba9d1 100644 --- a/app/static/js/nopaque/RessourceLists/CorpusFileList.js +++ b/app/static/js/nopaque/RessourceLists/CorpusFileList.js @@ -5,6 +5,7 @@ class CorpusFileList extends RessourceList { } init(user) { + console.log(user); this._init(user.corpora[this.corpusId].files); } diff --git a/app/templates/corpora/analyse_corpus.html.j2 b/app/templates/corpora/analyse_corpus.html.j2 index ccf682e0da1ad566c2df1a11c3a2cfcad9a7d825..8e69ce74c5d2d4ea7ab0a01ba86d5708e708a97e 100644 --- a/app/templates/corpora/analyse_corpus.html.j2 +++ b/app/templates/corpora/analyse_corpus.html.j2 @@ -65,7 +65,7 @@ <script src="{{ ASSET_URL }}"></script> {% endassets %} <script> -let corpusAnalysisApp = new CorpusAnalysisApp({{ corpus.id }}); +let corpusAnalysisApp = new CorpusAnalysisApp('{{ corpus.hashid }}'); let corpusAnalysisConcordance = new CorpusAnalysisConcordance(corpusAnalysisApp); let corpusAnalysisReader = new CorpusAnalysisReader(corpusAnalysisApp); corpusAnalysisApp.init();