JS codestyle enhancements

This commit is contained in:
Patrick Jentsch 2021-12-01 14:15:20 +01:00
parent 72ba61f369
commit 12ec6be60b
39 changed files with 490 additions and 288 deletions

View File

@ -6,36 +6,46 @@ class App {
this.socket.on('users.patch', patch => this.usersPatchHandler(patch)); this.socket.on('users.patch', patch => this.usersPatchHandler(patch));
} }
get users() {return this.data.users;} get users() {
return this.data.users;
}
addEventListener(type, listener) { addEventListener(type, listener) {
if (!(type in this.eventListeners)) {throw `Unknown event type: ${type}`;} if (!(type in this.eventListeners)) {
throw `Unknown event type: ${type}`;
}
this.eventListeners[type].push(listener); this.eventListeners[type].push(listener);
} }
flash(message, category) { flash(message, category) {
let toast, toastCloseActionElement; let iconPrefix;
let toast;
let toastCloseActionElement;
switch (category) { switch (category) {
case "corpus": case 'corpus':
message = `<i class="left material-icons">book</i>${message}`; iconPrefix = '<i class="left material-icons">book</i>';
break; break;
case "error": case 'error':
message = `<i class="left material-icons error-color-text">error</i>${message}`; iconPrefix = '<i class="error-color-text left material-icons">error</i>';
break; break;
case "job": case 'job':
message = `<i class="left nopaque-icons">J</i>${message}`; iconPrefix = '<i class="left nopaque-icons">J</i>';
break; break;
default: default:
message = `<i class="left material-icons">notifications</i>${message}`; iconPrefix = '<i class="left material-icons">notifications</i>';
break;
} }
toast = M.toast({ toast = M.toast(
html: ` {
<span>${message}</span> html: `
<button class="btn-flat toast-action white-text" data-action="close"> <span>${iconPrefix}${message}</span>
<i class="material-icons">close</i> <button class="btn-flat toast-action white-text" data-action="close">
</button> <i class="material-icons">close</i>
`.trim() </button>
}); `.trim()
}
);
toastCloseActionElement = toast.el.querySelector('.toast-action[data-action="close"]'); toastCloseActionElement = toast.el.querySelector('.toast-action[data-action="close"]');
toastCloseActionElement.addEventListener('click', () => {toast.dismiss();}); toastCloseActionElement.addEventListener('click', () => {toast.dismiss();});
} }
@ -55,8 +65,16 @@ class App {
} }
usersPatchHandler(patch) { usersPatchHandler(patch) {
let re, match, userId, ressourceId, jobId, relationship; let jobId;
for (let operation of patch.filter(operation => operation.op === 'add')) { 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)`); re = new RegExp(`^/users/([A-Za-z0-9]*)/corpora/([A-Za-z0-9]*)/(files)`);
if (re.test(operation.path)) { if (re.test(operation.path)) {
[match, userId, ressourceId, relationship] = operation.path.match(re); [match, userId, ressourceId, relationship] = operation.path.match(re);
@ -75,6 +93,6 @@ class App {
} }
} }
this.data = jsonpatch.apply_patch(this.data, patch); this.data = jsonpatch.apply_patch(this.data, patch);
for (let listener of this.eventListeners['users.patch']) {listener(patch);} for (listener of this.eventListeners['users.patch']) {listener(patch);}
} }
} }

View File

@ -4,7 +4,11 @@ class JobStatusNotifier {
} }
usersPatchHandler(patch) { usersPatchHandler(patch) {
let re, filteredPatch, match, jobId; let filteredPatch;
let jobId;
let match;
let re;
re = new RegExp(`^/users/${this.userId}/jobs/([A-Za-z0-9]*)/status$`) re = new RegExp(`^/users/${this.userId}/jobs/([A-Za-z0-9]*)/status$`)
filteredPatch = patch filteredPatch = patch
.filter(operation => operation.op === 'replace') .filter(operation => operation.op === 'replace')

View File

@ -2,34 +2,45 @@ class CorpusDisplay extends RessourceDisplay {
constructor(displayElement) { constructor(displayElement) {
super(displayElement); super(displayElement);
this.corpusId = displayElement.dataset.corpusId; this.corpusId = displayElement.dataset.corpusId;
for (let exportCorpusTriggerElement of this.displayElement.querySelectorAll('.export-corpus-trigger')) {
exportCorpusTriggerElement.addEventListener('click', () => this.requestCorpusExport());
}
app.socket.on(`export_corpus_${this.corpusId}`, () => this.downloadCorpus());
} }
init(user) { init(user) {
let corpus; let corpus;
corpus = user.corpora[this.corpusId]; corpus = user.corpora[this.corpusId];
this.setCreationDate(corpus.creation_date); this.setCreationDate(corpus.creation_date);
this.setDescription(corpus.description); this.setDescription(corpus.description);
this.setLastEditedDate(corpus.last_edited_date); this.setLastEditedDate(corpus.last_edited_date);
this.setStatus(corpus.status); this.setStatus(corpus.status);
this.setTitle(corpus.title); this.setTitle(corpus.title);
this.setTokenRatio(corpus.num_tokens, corpus.max_num_tokens); this.setNumTokens(corpus.num_tokens);
} }
patch(patch) { patch(patch) {
let re, filteredPatch; let filteredPatch;
let operation;
let re;
re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}`); re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}`);
filteredPatch = patch.filter(operation => re.test(operation.path)); filteredPatch = patch.filter(operation => re.test(operation.path));
for (let operation of filteredPatch) { for (operation of filteredPatch) {
switch(operation.op) { switch(operation.op) {
case 'replace': case 'replace':
re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/last_edited_date$`); re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/last_edited_date$`);
if (re.test(operation.path)) {this.setLastEditedDate(operation.value); break;} if (re.test(operation.path)) {
this.setLastEditedDate(operation.value);
break;
}
re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/num_tokens`);
if (re.test(operation.path)) {
this.numTokens(operation.value);
break;
}
re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/status$`); re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/status$`);
if (re.test(operation.path)) {this.status$(operation.value); break;} if (re.test(operation.path)) {
this.status(operation.value);
break;
}
break; break;
default: default:
break; break;
@ -37,55 +48,48 @@ class CorpusDisplay extends RessourceDisplay {
} }
} }
requestCorpusExport() {
app.socket.emit('export_corpus', app.users[this.userId].corpora[this.corpusId]);
app.flash('Preparing your corpus export...', 'corpus');
for (let exportCorpusTriggerElement of this.displayElement.querySelectorAll('.export-corpus-trigger')) {exportCorpusTriggerElement.classList.toggle('disabled', true);}
}
downloadCorpus() {
let downloadButton;
app.flash('Corpus download is ready!', 'corpus');
for (let exportCorpusTriggerElement of this.displayElement.querySelectorAll('.export-corpus-trigger')) {exportCorpusTriggerElement.classList.toggle('disabled', false);}
// Little trick to call the download view after ziping has finished
downloadButton = document.createElement('a');
downloadButton.href = `/corpora/${app.users[this.userId].corpora[this.corpusId]}/download`;
downloadButton.click();
}
setTitle(title) { setTitle(title) {
for (let element of this.displayElement.querySelectorAll('.corpus-title')) {this.setElement(element, title);} this.setElements(this.displayElement.querySelectorAll('.corpus-title'), title);
} }
setTokenRatio(numTokens, maxNumTokens) { setNumTokens(numTokens) {
for (let element of this.displayElement.querySelectorAll('.corpus-token-ratio')) {this.setElement(element, `${numTokens}/${maxNumTokens}`);} this.setElements(
this.displayElement.querySelectorAll('.corpus-token-ratio'),
`${numTokens}/${app.users[this.userId].corpora[this.corpusId].max_num_tokens}`
);
} }
setDescription(description) { setDescription(description) {
for (let element of this.displayElement.querySelectorAll('.corpus-description')) {this.setElement(element, description);} this.setElements(this.displayElement.querySelectorAll('.corpus-description'), description);
} }
setStatus(status) { setStatus(status) {
for (let element of this.displayElement.querySelectorAll('.analyse-corpus-trigger')) { let element;
let elements;
this.setElements(this.displayElement.querySelectorAll('.corpus-status'), status);
elements = this.displayElement.querySelectorAll('.analyse-corpus-trigger')
for (element of elements) {
if (['analysing', 'prepared', 'start analysis'].includes(status)) { if (['analysing', 'prepared', 'start analysis'].includes(status)) {
element.classList.remove('disabled'); element.classList.remove('disabled');
} else { } else {
element.classList.add('disabled'); element.classList.add('disabled');
} }
} }
for (let element of this.displayElement.querySelectorAll('.build-corpus-trigger')) { elements = this.displayElement.querySelectorAll('.build-corpus-trigger');
for (element of elements) {
if (status === 'unprepared' && Object.values(app.users[this.userId].corpora[this.corpusId].files).length > 0) { if (status === 'unprepared' && Object.values(app.users[this.userId].corpora[this.corpusId].files).length > 0) {
element.classList.remove('disabled'); element.classList.remove('disabled');
} else { } else {
element.classList.add('disabled'); element.classList.add('disabled');
} }
} }
for (let element of this.displayElement.querySelectorAll('.corpus-status')) {this.setElement(element, status);} elements = this.displayElement.querySelectorAll('.status');
for (let exportCorpusTriggerElement of this.displayElement.querySelectorAll('.export-corpus-trigger')) { for (element of elements) {
exportCorpusTriggerElement.classList.toggle('disabled', !['prepared', 'start analysis', 'stop analysis'].includes(status)); element.dataset.status = status;
} }
for (let element of this.displayElement.querySelectorAll('.status')) {element.dataset.status = status;} elements = this.displayElement.querySelectorAll('.status-spinner');
for (let element of this.displayElement.querySelectorAll('.status-spinner')) { for (element of elements) {
if (['submitted', 'queued', 'running', 'canceling', 'start analysis', 'stop analysis'].includes(status)) { if (['submitted', 'queued', 'running', 'canceling', 'start analysis', 'stop analysis'].includes(status)) {
element.classList.remove('hide'); element.classList.remove('hide');
} else { } else {
@ -95,14 +99,16 @@ class CorpusDisplay extends RessourceDisplay {
} }
setCreationDate(creationDate) { setCreationDate(creationDate) {
for (let element of this.displayElement.querySelectorAll('.corpus-creation-date')) { this.setElements(
this.setElement(element, creationDate.toLocaleString("en-US")); this.displayElement.querySelectorAll('.corpus-creation-date'),
} new Date(creationDate).toLocaleString("en-US")
);
} }
setLastEditedDate(lastEditedDate) { setLastEditedDate(lastEditedDate) {
for (let element of this.displayElement.querySelectorAll('.corpus-end-date')) { this.setElements(
this.setElement(element, lastEditedDate.toLocaleString("en-US")); this.displayElement.querySelectorAll('.corpus-end-date'),
} new Date(lastEditedDate).toLocaleString("en-US")
);
} }
} }

View File

@ -5,7 +5,9 @@ class JobDisplay extends RessourceDisplay {
} }
init(user) { init(user) {
let job = user.jobs[this.jobId]; let job;
job = user.jobs[this.jobId];
this.setCreationDate(job.creation_date); this.setCreationDate(job.creation_date);
this.setEndDate(job.creation_date); this.setEndDate(job.creation_date);
this.setDescription(job.description); this.setDescription(job.description);
@ -17,9 +19,13 @@ class JobDisplay extends RessourceDisplay {
} }
usersPatchHandler(patch) { usersPatchHandler(patch) {
let re = new RegExp(`^/users/${this.userId}/jobs/${this.jobId}`); let filteredPatch;
let filteredPatch = patch.filter(operation => re.test(operation.path)); let operation;
for (let operation of filteredPatch) { let re;
re = new RegExp(`^/users/${this.userId}/jobs/${this.jobId}`);
filteredPatch = patch.filter(operation => re.test(operation.path));
for (operation of filteredPatch) {
switch(operation.op) { switch(operation.op) {
case 'replace': case 'replace':
re = new RegExp(`^/users/${this.userId}/jobs/${this.jobId}/end_date$`); re = new RegExp(`^/users/${this.userId}/jobs/${this.jobId}/end_date$`);
@ -40,26 +46,33 @@ class JobDisplay extends RessourceDisplay {
} }
setTitle(title) { setTitle(title) {
for (let element of this.displayElement.querySelectorAll('.job-title')) {this.setElement(element, title);} this.setElements(this.displayElement.querySelectorAll('.job-title'), title);
} }
setDescription(description) { setDescription(description) {
for (let element of this.displayElement.querySelectorAll('.job-description')) {this.setElement(element, description);} this.setElements(this.displayElement.querySelectorAll('.job-description'), description);
} }
setStatus(status) { setStatus(status) {
for (let element of this.displayElement.querySelectorAll('.job-status')) { let element;
this.setElement(element, status); let elements;
this.setElements(this.displayElement.querySelectorAll('.job-status'), status);
elements = this.displayElement.querySelectorAll('.status');
for (element of elements) {
element.dataset.status = status;
} }
for (let element of this.displayElement.querySelectorAll('.status')) {element.dataset.status = status;} elements = this.displayElement.querySelectorAll('.status-spinner');
for (let element of this.displayElement.querySelectorAll('.status-spinner')) { for (element of elements) {
if (['complete', 'failed'].includes(status)) { if (['complete', 'failed'].includes(status)) {
element.classList.add('hide'); element.classList.add('hide');
} else { } else {
element.classList.remove('hide'); element.classList.remove('hide');
} }
} }
for (let element of this.displayElement.querySelectorAll('.restart-job-trigger')) { elements = this.displayElement.querySelectorAll('.restart-job-trigger');
for (element of elements) {
if (['complete', 'failed'].includes(status)) { if (['complete', 'failed'].includes(status)) {
element.classList.remove('hide'); element.classList.remove('hide');
} else { } else {
@ -69,26 +82,28 @@ class JobDisplay extends RessourceDisplay {
} }
setCreationDate(creationDate) { setCreationDate(creationDate) {
for (let element of this.displayElement.querySelectorAll('.job-creation-date')) { this.setElements(
this.setElement(element, creationDate.toLocaleString('en-US')); this.displayElement.querySelectorAll('.job-creation-date'),
} new Date(creationDate).toLocaleString('en-US')
);
} }
setEndDate(endDate) { setEndDate(endDate) {
for (let element of this.displayElement.querySelectorAll('.job-end-date')) { this.setElements(
this.setElement(element, endDate.toLocaleString('en-US')); this.displayElement.querySelectorAll('.job-end-date'),
} new Date(endDate).toLocaleString('en-US')
);
} }
setService(service) { setService(service) {
for (let element of this.displayElement.querySelectorAll('.job-service')) {this.setElement(element, service);} this.setElements(this.displayElement.querySelectorAll('.job-service'), service);
} }
setServiceArgs(serviceArgs) { setServiceArgs(serviceArgs) {
for (let element of this.displayElement.querySelectorAll('.job-service-args')) {this.setElement(element, serviceArgs);} this.setElements(this.displayElement.querySelectorAll('.job-service-args'), serviceArgs);
} }
setServiceVersion(serviceVersion) { setServiceVersion(serviceVersion) {
for (let element of this.displayElement.querySelectorAll('.job-service-version')) {this.setElement(element, serviceVersion);} this.setElements(this.displayElement.querySelectorAll('.job-service-version'), serviceVersion);
} }
} }

View File

@ -3,7 +3,7 @@ class RessourceDisplay {
this.displayElement = displayElement; this.displayElement = displayElement;
this.userId = this.displayElement.dataset.userId; this.userId = this.displayElement.dataset.userId;
app.addEventListener('users.patch', patch => this.usersPatchHandler(patch)); app.addEventListener('users.patch', patch => this.usersPatchHandler(patch));
app.getUserById(this.userId).then(user => this.init(user), error => {throw JSON.stringify(error);}); app.getUserById(this.userId).then(user => this.init(user));
} }
init(user) {throw 'Not implemented';} init(user) {throw 'Not implemented';}
@ -21,4 +21,12 @@ class RessourceDisplay {
break; break;
} }
} }
setElements(elements, value) {
let element;
for (element of elements) {
this.setElement(element, value);
}
}
} }

View File

@ -9,14 +9,23 @@ class CorpusFileList extends RessourceList {
} }
onclick(event) { onclick(event) {
let corpusFileElement = event.target.closest('tr[data-id]'); let action;
if (corpusFileElement === null) {throw 'Could not locate corpus file element';} let actionButtonElement;
let corpusFileId = corpusFileElement.dataset.id; let corpusFileElement;
let actionButtonElement = event.target.closest('.action-button[data-action]'); let corpusFileId;
let action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action; let deleteModal;
let deleteModalElement;
let tmp;
corpusFileElement = event.target.closest('tr[data-id]');
if (corpusFileElement === null) {return;}
corpusFileId = corpusFileElement.dataset.id;
actionButtonElement = event.target.closest('.action-button[data-action]');
action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action;
switch (action) { switch (action) {
case 'delete': case 'delete':
let deleteModalHTML = ` tmp = document.createElement('div');
tmp.innerHTML = `
<div class="modal"> <div class="modal">
<div class="modal-content"> <div class="modal-content">
<h4>Confirm corpus deletion</h4> <h4>Confirm corpus deletion</h4>
@ -28,10 +37,16 @@ class CorpusFileList extends RessourceList {
</div> </div>
</div> </div>
`.trim(); `.trim();
let deleteModalParentElement = document.querySelector('#modals'); deleteModalElement = document.querySelector('#modals').appendChild(tmp.firstChild);
deleteModalParentElement.insertAdjacentHTML('beforeend', deleteModalHTML); deleteModal = M.Modal.init(
let deleteModalElement = deleteModalParentElement.lastChild; deleteModalElement,
let deleteModal = M.Modal.init(deleteModalElement, {onCloseEnd: () => {deleteModal.destroy(); deleteModalElement.remove();}}); {
onCloseEnd: () => {
deleteModal.destroy();
deleteModalElement.remove();
}
}
);
deleteModal.open(); deleteModal.open();
break; break;
case 'download': case 'download':
@ -46,13 +61,22 @@ class CorpusFileList extends RessourceList {
} }
usersPatchHandler(patch) { usersPatchHandler(patch) {
let re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/files/([A-Za-z0-9]*)`); let corpusFileId;
let filteredPatch = patch.filter(operation => re.test(operation.path)); let filteredPatch;
for (let operation of filteredPatch) { let match;
let operation;
let re;
let valueName;
re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/files/([A-Za-z0-9]*)`);
filteredPatch = patch.filter(operation => re.test(operation.path));
for (operation of filteredPatch) {
switch(operation.op) { switch(operation.op) {
case 'add': case 'add':
re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/files/([A-Za-z0-9]*)$`); re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/files/([A-Za-z0-9]*)$`);
if (re.test(operation.path)) {this.add(operation.value);} if (re.test(operation.path)) {
this.add(operation.value);
}
break; break;
case 'remove': case 'remove':
re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/files/([A-Za-z0-9]*)$`); re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/files/([A-Za-z0-9]*)$`);

View File

@ -8,14 +8,23 @@ class CorpusList extends RessourceList {
} }
onclick(event) { onclick(event) {
let corpusElement = event.target.closest('tr[data-id]'); let action;
if (corpusElement === null) {throw 'Could not locate corpus element';} let actionButtonElement;
let corpusId = corpusElement.dataset.id; let corpusElement;
let actionButtonElement = event.target.closest('.action-button[data-action]'); let corpusId;
let action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action; let deleteModal;
let deleteModalElement;
let tmp;
corpusElement = event.target.closest('tr[data-id]');
if (corpusElement === null) {return;}
corpusId = corpusElement.dataset.id;
actionButtonElement = event.target.closest('.action-button[data-action]');
action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action;
switch (action) { switch (action) {
case 'delete': case 'delete':
let deleteModalHTML = ` tmp = document.createElement('div');
tmp.innerHTML = `
<div class="modal"> <div class="modal">
<div class="modal-content"> <div class="modal-content">
<h4>Confirm corpus deletion</h4> <h4>Confirm corpus deletion</h4>
@ -27,10 +36,16 @@ class CorpusList extends RessourceList {
</div> </div>
</div> </div>
`.trim(); `.trim();
let deleteModalParentElement = document.querySelector('#modals'); deleteModalElement = document.querySelector('#modals').appendChild(tmp.firstChild);
deleteModalParentElement.insertAdjacentHTML('beforeend', deleteModalHTML); deleteModal = M.Modal.init(
let deleteModalElement = deleteModalParentElement.lastChild; deleteModalElement,
let deleteModal = M.Modal.init(deleteModalElement, {onCloseEnd: () => {deleteModal.destroy(); deleteModalElement.remove();}}); {
onCloseEnd: () => {
deleteModal.destroy();
deleteModalElement.remove();
}
}
);
deleteModal.open(); deleteModal.open();
break; break;
case 'view': case 'view':
@ -42,9 +57,16 @@ class CorpusList extends RessourceList {
} }
usersPatchHandler(patch) { usersPatchHandler(patch) {
let re = new RegExp(`^/users/${this.userId}/corpora/([A-Za-z0-9]*)`); let corpusId;
let filteredPatch = patch.filter(operation => re.test(operation.path)); let filteredPatch;
for (let operation of filteredPatch) { let match;
let operation;
let re;
let valueName;
re = new RegExp(`^/users/${this.userId}/corpora/([A-Za-z0-9]*)`);
filteredPatch = patch.filter(operation => re.test(operation.path));
for (operation of filteredPatch) {
switch(operation.op) { switch(operation.op) {
case 'add': case 'add':
re = new RegExp(`^/users/${this.userId}/corpora/([A-Za-z0-9]*)$`); re = new RegExp(`^/users/${this.userId}/corpora/([A-Za-z0-9]*)$`);
@ -53,14 +75,14 @@ class CorpusList extends RessourceList {
case 'remove': case 'remove':
re = new RegExp(`^/users/${this.userId}/corpora/([A-Za-z0-9]*)$`); re = new RegExp(`^/users/${this.userId}/corpora/([A-Za-z0-9]*)$`);
if (re.test(operation.path)) { if (re.test(operation.path)) {
let [match, corpusId] = operation.path.match(re); [match, corpusId] = operation.path.match(re);
this.remove(corpusId); this.remove(corpusId);
} }
break; break;
case 'replace': case 'replace':
re = new RegExp(`^/users/${this.userId}/corpora/([A-Za-z0-9]*)/(status|description|title)$`); re = new RegExp(`^/users/${this.userId}/corpora/([A-Za-z0-9]*)/(status|description|title)$`);
if (re.test(operation.path)) { if (re.test(operation.path)) {
let [match, corpusId, valueName] = operation.path.match(re); [match, corpusId, valueName] = operation.path.match(re);
this.replace(corpusId, valueName, operation.value); this.replace(corpusId, valueName, operation.value);
} }
break; break;

View File

@ -9,12 +9,17 @@ class JobInputList extends RessourceList {
} }
onclick(event) { onclick(event) {
let jobInputElement = event.target.closest('tr[data-id]'); let jobInputElement;
let jobInputId;
let action;
let actionButtonElement;
jobInputElement = event.target.closest('tr[data-id]');
if (jobInputElement === null) {return;} if (jobInputElement === null) {return;}
let jobInputId = jobInputElement.dataset.id; jobInputId = jobInputElement.dataset.id;
let actionButtonElement = event.target.closest('.action-button[data-action]'); actionButtonElement = event.target.closest('.action-button[data-action]');
if (actionButtonElement === null) {return;} if (actionButtonElement === null) {return;}
let action = actionButtonElement.dataset.action; action = actionButtonElement.dataset.action;
switch (action) { switch (action) {
case 'download': case 'download':
window.location.href = `/jobs/${this.jobId}/inputs/${jobInputId}/download`; window.location.href = `/jobs/${this.jobId}/inputs/${jobInputId}/download`;

View File

@ -8,14 +8,23 @@ class JobList extends RessourceList {
} }
onclick(event) { onclick(event) {
let jobElement = event.target.closest('tr[data-id]'); let action;
if (jobElement === null) {throw 'Could not locate job element';} let actionButtonElement;
let jobId = jobElement.dataset.id; let deleteModal;
let actionButtonElement = event.target.closest('.action-button[data-action]'); let deleteModalElement;
let action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action; let jobElement;
let jobId;
let tmp;
jobElement = event.target.closest('tr[data-id]');
if (jobElement === null) {return;}
jobId = jobElement.dataset.id;
actionButtonElement = event.target.closest('.action-button[data-action]');
action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action;
switch (action) { switch (action) {
case 'delete': case 'delete':
let deleteModalHTML = ` tmp = document.createElement('div');
tmp.innerHTML = `
<div class="modal"> <div class="modal">
<div class="modal-content"> <div class="modal-content">
<h4>Confirm job deletion</h4> <h4>Confirm job deletion</h4>
@ -27,10 +36,16 @@ class JobList extends RessourceList {
</div> </div>
</div> </div>
`.trim(); `.trim();
let deleteModalParentElement = document.querySelector('#modals'); deleteModalElement = document.querySelector('#modals').appendChild(tmp.firstChild);
deleteModalParentElement.insertAdjacentHTML('beforeend', deleteModalHTML); deleteModal = M.Modal.init(
let deleteModalElement = deleteModalParentElement.lastChild; deleteModalElement,
let deleteModal = M.Modal.init(deleteModalElement, {onCloseEnd: () => {deleteModal.destroy(); deleteModalElement.remove();}}); {
onCloseEnd: () => {
deleteModal.destroy();
deleteModalElement.remove();
}
}
);
deleteModal.open(); deleteModal.open();
break; break;
case 'view': case 'view':
@ -42,25 +57,34 @@ class JobList extends RessourceList {
} }
usersPatchHandler(patch) { usersPatchHandler(patch) {
let re = new RegExp(`^/users/${this.userId}/jobs/([A-Za-z0-9]*)`); let filteredPatch;
let filteredPatch = patch.filter(operation => re.test(operation.path)); let jobId;
for (let operation of filteredPatch) { let match;
let operation;
let re;
let valueName;
re = new RegExp(`^/users/${this.userId}/jobs/([A-Za-z0-9]*)`);
filteredPatch = patch.filter(operation => re.test(operation.path));
for (operation of filteredPatch) {
switch(operation.op) { switch(operation.op) {
case 'add': case 'add':
re = new RegExp(`^/users/${this.userId}/jobs/([A-Za-z0-9]*)$`); re = new RegExp(`^/users/${this.userId}/jobs/([A-Za-z0-9]*)$`);
if (re.test(operation.path)) {this.add(operation.value);} if (re.test(operation.path)) {
this.add(operation.value);
}
break; break;
case 'remove': case 'remove':
re = new RegExp(`^/users/${this.userId}/jobs/([A-Za-z0-9]*)$`); re = new RegExp(`^/users/${this.userId}/jobs/([A-Za-z0-9]*)$`);
if (re.test(operation.path)) { if (re.test(operation.path)) {
let [match, jobId] = operation.path.match(re); [match, jobId] = operation.path.match(re);
this.remove(jobId); this.remove(jobId);
} }
break; break;
case 'replace': case 'replace':
re = new RegExp(`^/users/${this.userId}/jobs/([A-Za-z0-9]*)/(service|status|description|title)$`); re = new RegExp(`^/users/${this.userId}/jobs/([A-Za-z0-9]*)/(service|status|description|title)$`);
if (re.test(operation.path)) { if (re.test(operation.path)) {
let [match, jobId, valueName] = operation.path.match(re); [match, jobId, valueName] = operation.path.match(re);
this.replace(jobId, valueName, operation.value); this.replace(jobId, valueName, operation.value);
} }
break; break;

View File

@ -9,12 +9,17 @@ class JobResultList extends RessourceList {
} }
onclick(event) { onclick(event) {
let jobResultElement = event.target.closest('tr[data-id]'); let action;
let actionButtonElement;
let jobResultElement;
let jobResultId;
jobResultElement = event.target.closest('tr[data-id]');
if (jobResultElement === null) {return;} if (jobResultElement === null) {return;}
let jobResultId = jobResultElement.dataset.id; jobResultId = jobResultElement.dataset.id;
let actionButtonElement = event.target.closest('.action-button[data-action]'); actionButtonElement = event.target.closest('.action-button[data-action]');
if (actionButtonElement === null) {return;} if (actionButtonElement === null) {return;}
let action = actionButtonElement.dataset.action; action = actionButtonElement.dataset.action;
switch (action) { switch (action) {
case 'download': case 'download':
window.location.href = `/jobs/${this.jobId}/results/${jobResultId}`; window.location.href = `/jobs/${this.jobId}/results/${jobResultId}`;
@ -25,13 +30,19 @@ class JobResultList extends RessourceList {
} }
usersPatchHandler(patch) { usersPatchHandler(patch) {
let re = new RegExp(`^/users/${this.userId}/jobs/${this.jobId}/results/([A-Za-z0-9]*)`); let filteredPatch;
let filteredPatch = patch.filter(operation => re.test(operation.path)); let operation;
for (let operation of filteredPatch) { let re;
re = new RegExp(`^/users/${this.userId}/jobs/${this.jobId}/results/([A-Za-z0-9]*)`);
filteredPatch = patch.filter(operation => re.test(operation.path));
for (operation of filteredPatch) {
switch(operation.op) { switch(operation.op) {
case 'add': case 'add':
re = new RegExp(`^/users/${this.userId}/jobs/${this.jobId}/results/([A-Za-z0-9]*)$`); re = new RegExp(`^/users/${this.userId}/jobs/${this.jobId}/results/([A-Za-z0-9]*)$`);
if (re.test(operation.path)) {this.add(operation.value);} if (re.test(operation.path)) {
this.add(operation.value);
}
break; break;
default: default:
break; break;
@ -41,6 +52,7 @@ class JobResultList extends RessourceList {
preprocessRessource(jobResult) { preprocessRessource(jobResult) {
let description; let description;
if (jobResult.filename.endsWith('.pdf.zip')) { if (jobResult.filename.endsWith('.pdf.zip')) {
description = 'PDF files with text layer'; description = 'PDF files with text layer';
} else if (jobResult.filename.endsWith('.txt.zip')) { } else if (jobResult.filename.endsWith('.txt.zip')) {

View File

@ -8,14 +8,23 @@ class QueryResultList extends RessourceList {
} }
onclick(event) { onclick(event) {
let queryResultElement = event.target.closest('tr[data-id]'); let action;
let actionButtonElement;
let deleteModal;
let deleteModalElement;
let queryResultElement;
let queryResultId;
let tmp;
queryResultElement = event.target.closest('tr[data-id]');
if (queryResultElement === null) {return;} if (queryResultElement === null) {return;}
let queryResultId = queryResultElement.dataset.id; queryResultId = queryResultElement.dataset.id;
let actionButtonElement = event.target.closest('.action-button[data-action]'); actionButtonElement = event.target.closest('.action-button[data-action]');
let action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action; action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action;
switch (action) { switch (action) {
case 'delete': case 'delete':
let deleteModalHTML = ` tmp = document.createElement('div');
tmp.innerHTML = `
<div class="modal"> <div class="modal">
<div class="modal-content"> <div class="modal-content">
<h4>Confirm query result deletion</h4> <h4>Confirm query result deletion</h4>
@ -27,10 +36,16 @@ class QueryResultList extends RessourceList {
</div> </div>
</div> </div>
`.trim(); `.trim();
let deleteModalParentElement = document.querySelector('#modals'); deleteModalElement = document.querySelector('#modals').appendChild(tmp.firstChild);
deleteModalParentElement.insertAdjacentHTML('beforeend', deleteModalHTML); deleteModal = M.Modal.init(
let deleteModalElement = deleteModalParentElement.lastChild; deleteModalElement,
let deleteModal = M.Modal.init(deleteModalElement, {onCloseEnd: () => {deleteModal.destroy(); deleteModalElement.remove();}}); {
onCloseEnd: () => {
deleteModal.destroy();
deleteModalElement.remove();
}
}
);
deleteModal.open(); deleteModal.open();
break; break;
case 'view': case 'view':
@ -42,25 +57,34 @@ class QueryResultList extends RessourceList {
} }
usersPatchHandler(patch) { usersPatchHandler(patch) {
let re = new RegExp(`^/users/${this.userId}/query_results/([A-Za-z0-9]*)`); let filteredPatch;
let filteredPatch = patch.filter(operation => re.test(operation.path)); let match;
for (let operation of filteredPatch) { let operation;
let queryResultId;
let re;
let valueName;
re = new RegExp(`^/users/${this.userId}/query_results/([A-Za-z0-9]*)`);
filteredPatch = patch.filter(operation => re.test(operation.path));
for (operation of filteredPatch) {
switch(operation.op) { switch(operation.op) {
case 'add': case 'add':
re = new RegExp(`^/users/${this.userId}/query_results/([A-Za-z0-9]*)$`); re = new RegExp(`^/users/${this.userId}/query_results/([A-Za-z0-9]*)$`);
if (re.test(operation.path)) {this.add(operation.value);} if (re.test(operation.path)) {
this.add(operation.value);
}
break; break;
case 'remove': case 'remove':
re = new RegExp(`^/users/${this.userId}/query_results/([A-Za-z0-9]*)$`); re = new RegExp(`^/users/${this.userId}/query_results/([A-Za-z0-9]*)$`);
if (re.test(operation.path)) { if (re.test(operation.path)) {
let [match, queryResultId] = operation.path.match(re); [match, queryResultId] = operation.path.match(re);
this.remove(queryResultId); this.remove(queryResultId);
} }
break; break;
case 'replace': case 'replace':
re = new RegExp(`^/users/${this.userId}/query_results/([A-Za-z0-9]*)/(corpus_title|description|query|title)$`); re = new RegExp(`^/users/${this.userId}/query_results/([A-Za-z0-9]*)/(corpus_title|description|query|title)$`);
if (re.test(operation.path)) { if (re.test(operation.path)) {
let [match, queryResultId, valueName] = operation.path.match(re); [match, queryResultId, valueName] = operation.path.match(re);
this.replace(queryResultId, valueName, operation.value); this.replace(queryResultId, valueName, operation.value);
} }
break; break;

View File

@ -8,14 +8,23 @@ class UserList extends RessourceList {
} }
onclick(event) { onclick(event) {
let userElement = event.target.closest('tr[data-id]'); let action;
let actionButtonElement;
let deleteModal;
let deleteModalElement;
let tmp;
let userElement;
let userId;
userElement = event.target.closest('tr[data-id]');
if (userElement === null) {return;} if (userElement === null) {return;}
let userId = userElement.dataset.id; userId = userElement.dataset.id;
let actionButtonElement = event.target.closest('.action-button[data-action]'); actionButtonElement = event.target.closest('.action-button[data-action]');
let action = (actionButtonElement === null) ? 'view' : actionButtonElement.dataset.action; action = (actionButtonElement === null) ? 'view' : actionButtonElement.dataset.action;
switch (action) { switch (action) {
case 'delete': case 'delete':
let deleteModalHTML = ` tmp = document.createElement('div');
tmp.innerHTML = `
<div class="modal"> <div class="modal">
<div class="modal-content"> <div class="modal-content">
<h4>Confirm user deletion</h4> <h4>Confirm user deletion</h4>
@ -27,10 +36,16 @@ class UserList extends RessourceList {
</div> </div>
</div> </div>
`.trim(); `.trim();
let deleteModalParentElement = document.querySelector('#modals'); deleteModalElement = document.querySelector('#modals').appendChild(tmp.firstChild);
deleteModalParentElement.insertAdjacentHTML('beforeend', deleteModalHTML); deleteModal = M.Modal.init(
let deleteModalElement = deleteModalParentElement.lastChild; deleteModalElement,
let deleteModal = M.Modal.init(deleteModalElement, {onCloseEnd: () => {deleteModal.destroy(); deleteModalElement.remove();}}); {
onCloseEnd: () => {
deleteModal.destroy();
deleteModalElement.remove();
}
}
);
deleteModal.open(); deleteModal.open();
break; break;
case 'edit': case 'edit':
@ -50,7 +65,7 @@ class UserList extends RessourceList {
id_: user.id, id_: user.id,
username: user.username, username: user.username,
email: user.email, email: user.email,
last_seen: user.last_seen.toLocaleString("en-US"), last_seen: new Date(user.last_seen).toLocaleString("en-US"),
role: user.role.name role: user.role.name
}; };
} }

View File

@ -68,4 +68,4 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock page_content %}

View File

@ -92,9 +92,11 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock page_content %}
<!-- Modals --> {% block modals %}
{{ super() }}
<div id="delete-user-modal" class="modal"> <div id="delete-user-modal" class="modal">
<div class="modal-content"> <div class="modal-content">
<h3>Delete user</h3> <h3>Delete user</h3>
@ -105,8 +107,7 @@
<a href="{{ url_for('.delete_user', user_id=user.id) }}" class="modal-close waves-effect waves-light btn red"><i class="material-icons left">delete</i>Delete</a> <a href="{{ url_for('.delete_user', user_id=user.id) }}" class="modal-close waves-effect waves-light btn red"><i class="material-icons left">delete</i>Delete</a>
</div> </div>
</div> </div>
{% endblock %} {% endblock modals %}
{% block scripts %} {% block scripts %}
{{ super() }} {{ super() }}

View File

@ -35,7 +35,7 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock page_content %}
{% block scripts %} {% block scripts %}
{{ super() }} {{ super() }}

View File

@ -52,4 +52,4 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock page_content %}

View File

@ -45,4 +45,4 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock page_content %}

View File

@ -29,4 +29,4 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock page_content %}

View File

@ -28,4 +28,4 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock page_content %}

View File

@ -23,4 +23,4 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock page_content %}

View File

@ -32,13 +32,13 @@
{% block main_attribs %} class="background-color"{% endblock main_attribs %} {% block main_attribs %} class="background-color"{% endblock main_attribs %}
{% block main %} {% block main %}
{% block page_content %}{% endblock page_content %} {% block page_content %}{% endblock page_content %}
{% block modals %}
<div id="modals"> <div id="modals">
{% if current_user.is_authenticated %} {% block modals %}
{% include "_roadmap.html.j2" %} {% if current_user.is_authenticated %}
{% endif %} {% include "_roadmap.html.j2" %}
{% endif %}
{% endblock modals %}
</div> </div>
{% endblock modals %}
{% endblock main %} {% endblock main %}
{% block footer_attribs %} class="page-footer primary-variant-color"{% endblock footer_attribs %} {% block footer_attribs %} class="page-footer primary-variant-color"{% endblock footer_attribs %}

View File

@ -38,4 +38,4 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock page_content %}

View File

@ -71,7 +71,11 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock page_content %}
{% block modals %}
{{ super() }}
<div id="progress-modal" class="modal"> <div id="progress-modal" class="modal">
<div class="modal-content"> <div class="modal-content">
<h4><i class="material-icons prefix">file_upload</i> Uploading file...</h4> <h4><i class="material-icons prefix">file_upload</i> Uploading file...</h4>
@ -83,4 +87,4 @@
<a href="#!" class="modal-close waves-effect waves-light btn red abort-request">Cancel</a> <a href="#!" class="modal-close waves-effect waves-light btn red abort-request">Cancel</a>
</div> </div>
</div> </div>
{% endblock %} {% endblock modals %}

View File

@ -70,17 +70,6 @@
<a class="btn modal-trigger red waves-effect waves-light" data-target="delete-corpus-modal"><i class="material-icons left">delete</i>Delete</a> <a class="btn modal-trigger red waves-effect waves-light" data-target="delete-corpus-modal"><i class="material-icons left">delete</i>Delete</a>
</div> </div>
</div> </div>
<div id="delete-corpus-modal" class="modal">
<div class="modal-content">
<h4>Confirm corpus deletion</h4>
<p>Do you really want to delete the corpus <span class="corpus-title"></span>? All files will be permanently deleted!</p>
</div>
<div class="modal-footer">
<a class="btn modal-close waves-effect waves-light" href="#!">Cancel</a>
<a class="btn modal-close red waves-effect waves-light" href="{{ url_for('corpora.delete_corpus', corpus_id=corpus.id) }}"><i class="material-icons left">delete</i>Delete</a>
</div>
</div>
</div> </div>
<div class="col s12" id="corpus-files" data-corpus-id="{{ corpus.hashid }}" data-user-id="{{ corpus.user.hashid }}"> <div class="col s12" id="corpus-files" data-corpus-id="{{ corpus.hashid }}" data-user-id="{{ corpus.user.hashid }}">
@ -115,6 +104,20 @@
</div> </div>
{% endblock page_content %} {% endblock page_content %}
{% block modals %}
{{ super() }}
<div id="delete-corpus-modal" class="modal">
<div class="modal-content">
<h4>Confirm corpus deletion</h4>
<p>Do you really want to delete the corpus <span class="corpus-title"></span>? All files will be permanently deleted!</p>
</div>
<div class="modal-footer">
<a class="btn modal-close waves-effect waves-light" href="#!">Cancel</a>
<a class="btn modal-close red waves-effect waves-light" href="{{ url_for('corpora.delete_corpus', corpus_id=corpus.id) }}"><i class="material-icons left">delete</i>Delete</a>
</div>
</div>
{% endblock modals %}
{% block scripts %} {% block scripts %}
{{ super() }} {{ super() }}
<script> <script>

View File

@ -48,4 +48,4 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock page_content %}

View File

@ -43,7 +43,10 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock page_content %}
{% block modals %}
{{ super() }}
<div id="progress-modal" class="modal"> <div id="progress-modal" class="modal">
<div class="modal-content"> <div class="modal-content">
<h4><i class="material-icons prefix">file_upload</i> Uploading file...</h4> <h4><i class="material-icons prefix">file_upload</i> Uploading file...</h4>
@ -55,4 +58,4 @@
<a href="#!" class="modal-close waves-effect waves-light btn red abort-request">Cancel</a> <a href="#!" class="modal-close waves-effect waves-light btn red abort-request">Cancel</a>
</div> </div>
</div> </div>
{% endblock %} {% endblock modals %}

View File

@ -85,30 +85,6 @@
<a class="btn modal-trigger red waves-effect waves-light" data-target="delete-job-modal"><i class="material-icons left">delete</i>Delete</a> <a class="btn modal-trigger red waves-effect waves-light" data-target="delete-job-modal"><i class="material-icons left">delete</i>Delete</a>
</div> </div>
</div> </div>
<div id="delete-job-modal" class="modal">
<div class="modal-content">
<h4>Confirm deletion</h4>
<p>Do you really want to delete the job <span class="job-title"></span>? All associated files will be permanently deleted.</p>
</div>
<div class="modal-footer">
<a href="#!" class="btn modal-close waves-effect waves-light">Cancel</a>
<a class="btn modal-close red waves-effect waves-light" href="{{ url_for('jobs.delete_job', job_id=job.id) }}"><i class="material-icons left">delete</i>Delete</a>
</div>
</div>
{% if current_user.is_administrator() %}
<div id="restart-job-modal" class="modal">
<div class="modal-content">
<h4>Confirm restart</h4>
<p>Do you really want to restart the job <span class="job-title"></span>? All log and result files will be permanently deleted.</p>
</div>
<div class="modal-footer">
<a href="#!" class="btn modal-close waves-effect waves-light">Cancel</a>
<a class="btn modal-close red waves-effect waves-light" href="{{ url_for('jobs.restart', job_id=job.id) }}"><i class="material-icons left">restart</i>Restart</a>
</div>
</div>
{% endif %}
</div> </div>
<div class="col s12" id="job-inputs" data-job-id="{{ job.hashid }}" data-user-id="{{ job.user.hashid }}"> <div class="col s12" id="job-inputs" data-job-id="{{ job.hashid }}" data-user-id="{{ job.user.hashid }}">
@ -165,6 +141,33 @@
</div> </div>
{% endblock page_content %} {% endblock page_content %}
{% block modals %}
{{ super() }}
<div id="delete-job-modal" class="modal">
<div class="modal-content">
<h4>Confirm deletion</h4>
<p>Do you really want to delete the job <span class="job-title"></span>? All associated files will be permanently deleted.</p>
</div>
<div class="modal-footer">
<a href="#!" class="btn modal-close waves-effect waves-light">Cancel</a>
<a class="btn modal-close red waves-effect waves-light" href="{{ url_for('jobs.delete_job', job_id=job.id) }}"><i class="material-icons left">delete</i>Delete</a>
</div>
</div>
{% if current_user.is_administrator() %}
<div id="restart-job-modal" class="modal">
<div class="modal-content">
<h4>Confirm restart</h4>
<p>Do you really want to restart the job <span class="job-title"></span>? All log and result files will be permanently deleted.</p>
</div>
<div class="modal-footer">
<a href="#!" class="btn modal-close waves-effect waves-light">Cancel</a>
<a class="btn modal-close red waves-effect waves-light" href="{{ url_for('jobs.restart', job_id=job.id) }}"><i class="material-icons left">restart</i>Restart</a>
</div>
</div>
{% endif %}
{% endblock modals %}
{% block scripts %} {% block scripts %}
{{ super() }} {{ super() }}
<script> <script>

View File

@ -119,58 +119,61 @@
<p><a class="modal-trigger waves-effect waves-light btn" href="#" data-target="new-job-modal"><i class="material-icons left">add</i>New job</a></p> <p><a class="modal-trigger waves-effect waves-light btn" href="#" data-target="new-job-modal"><i class="material-icons left">add</i>New job</a></p>
</div> </div>
</div> </div>
<div id="new-job-modal" class="modal">
<div class="modal-content">
<h4>Select a service</h4>
<p>&nbsp;</p>
<div class="row">
<div class="col s12 m4">
<div class="card-panel center-align hoverable">
<br>
<a href="{{ url_for('services.service', service='file-setup') }}" class="btn-floating btn-large waves-effect waves-light" style="transform: scale(2);">
<i class="nopaque-icons service-color darken service-icon" data-service="file-setup"></i>
</a>
<br><br>
<p class="service-color-text darken" data-service="file-setup"><b>File setup</b></p>
<p class="light">Digital copies of text based research data (books, letters, etc.) often comprise various files and formats. nopaque converts and merges those files to facilitate further processing and the application of other services.</p>
<a href="{{ url_for('services.service', service='file-setup') }}" class="waves-effect waves-light btn service-color darken" data-service="file-setup">Create Job</a>
</div>
</div>
<div class="col s12 m4">
<div class="card-panel center-align hoverable">
<br>
<a href="{{ url_for('services.service', service='ocr') }}" class="btn-floating btn-large waves-effect waves-light" style="transform: scale(2);">
<i class="nopaque-icons service-color darken service-icon" data-service="ocr" style="font-size: 2.5rem;"></i>
</a>
<br><br>
<p class="service-color-text darken" data-service="ocr"><b>Optical Character Recognition</b></p>
<p class="light">nopaque converts your image data like photos or scans into text data through a process called OCR. This step enables you to proceed with further computational analysis of your documents.</p>
<a href="{{ url_for('services.service', service='ocr') }}" class="waves-effect waves-light btn service-color darken" data-service="ocr">Create Job</a>
</div>
</div>
<div class="col s12 m4">
<div class="card-panel center-align hoverable">
<br>
<a href="{{ url_for('services.service', service='nlp') }}" class="btn-floating btn-large waves-effect waves-light" style="transform: scale(2);">
<i class="nopaque-icons service-color darken service-icon" data-service="nlp" style="font-size: 2.5rem;"></i>
</a>
<br><br>
<p class="service-color-text darken" data-service="nlp"><b>Natural Language Processing</b></p>
<p class="light">By means of computational linguistic data processing (tokenization, lemmatization, part-of-speech tagging and named-entity recognition) nopaque extracts additional information from your text.</p>
<a href="{{ url_for('services.service', service='nlp') }}" class="waves-effect waves-light btn service-color darken" data-service="nlp">Create Job</a>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<a href="#!" class="modal-close waves-effect waves-light btn-flat">Close</a>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock page_content %}
{% block modals %}
{{ super() }}
<div id="new-job-modal" class="modal">
<div class="modal-content">
<h4>Select a service</h4>
<p>&nbsp;</p>
<div class="row">
<div class="col s12 m4">
<div class="card-panel center-align hoverable">
<br>
<a href="{{ url_for('services.service', service='file-setup') }}" class="btn-floating btn-large waves-effect waves-light" style="transform: scale(2);">
<i class="nopaque-icons service-color darken service-icon" data-service="file-setup"></i>
</a>
<br><br>
<p class="service-color-text darken" data-service="file-setup"><b>File setup</b></p>
<p class="light">Digital copies of text based research data (books, letters, etc.) often comprise various files and formats. nopaque converts and merges those files to facilitate further processing and the application of other services.</p>
<a href="{{ url_for('services.service', service='file-setup') }}" class="waves-effect waves-light btn service-color darken" data-service="file-setup">Create Job</a>
</div>
</div>
<div class="col s12 m4">
<div class="card-panel center-align hoverable">
<br>
<a href="{{ url_for('services.service', service='ocr') }}" class="btn-floating btn-large waves-effect waves-light" style="transform: scale(2);">
<i class="nopaque-icons service-color darken service-icon" data-service="ocr" style="font-size: 2.5rem;"></i>
</a>
<br><br>
<p class="service-color-text darken" data-service="ocr"><b>Optical Character Recognition</b></p>
<p class="light">nopaque converts your image data like photos or scans into text data through a process called OCR. This step enables you to proceed with further computational analysis of your documents.</p>
<a href="{{ url_for('services.service', service='ocr') }}" class="waves-effect waves-light btn service-color darken" data-service="ocr">Create Job</a>
</div>
</div>
<div class="col s12 m4">
<div class="card-panel center-align hoverable">
<br>
<a href="{{ url_for('services.service', service='nlp') }}" class="btn-floating btn-large waves-effect waves-light" style="transform: scale(2);">
<i class="nopaque-icons service-color darken service-icon" data-service="nlp" style="font-size: 2.5rem;"></i>
</a>
<br><br>
<p class="service-color-text darken" data-service="nlp"><b>Natural Language Processing</b></p>
<p class="light">By means of computational linguistic data processing (tokenization, lemmatization, part-of-speech tagging and named-entity recognition) nopaque extracts additional information from your text.</p>
<a href="{{ url_for('services.service', service='nlp') }}" class="waves-effect waves-light btn service-color darken" data-service="nlp">Create Job</a>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<a href="#!" class="modal-close waves-effect waves-light btn-flat">Close</a>
</div>
</div>
{% endblock modals %}
{% block scripts %} {% block scripts %}
{{ super() }} {{ super() }}

View File

@ -53,4 +53,4 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock page_content %}

View File

@ -215,4 +215,4 @@
<img src="{{ url_for('static', filename='images/parallax_lq/05_chapter_book_text_tale.jpg') }}" alt=""> <img src="{{ url_for('static', filename='images/parallax_lq/05_chapter_book_text_tale.jpg') }}" alt="">
</div> </div>
</div> </div>
{% endblock %} {% endblock page_content %}

View File

@ -48,4 +48,4 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock page_content %}

View File

@ -152,4 +152,4 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock page_content %}

View File

@ -109,4 +109,4 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock page_content %}

View File

@ -1,7 +1,7 @@
{% block doc %} {% block doc %}
<!DOCTYPE html> <!DOCTYPE html>
<html{% block html_attribs %}{% endblock html_attribs %}> <html{% block html_attribs %}{% endblock html_attribs %}>
{% block html %} {% block html %}
<head> <head>
{% block head %} {% block head %}
<title>{% block title %}{{title|default}}{% endblock title %}</title> <title>{% block title %}{{title|default}}{% endblock title %}</title>
@ -40,6 +40,6 @@
{% endblock scripts %} {% endblock scripts %}
{% endblock body %} {% endblock body %}
</body> </body>
{% endblock html %} {% endblock html %}
</html> </html>
{% endblock doc %} {% endblock doc %}

View File

@ -88,7 +88,7 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock page_content %}
{% block scripts %} {% block scripts %}
{{ super() }} {{ super() }}

View File

@ -65,7 +65,10 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock page_content %}
{% block modals %}
{{ super() }}
<div id="progress-modal" class="modal"> <div id="progress-modal" class="modal">
<div class="modal-content"> <div class="modal-content">
<h4><i class="material-icons prefix">file_upload</i> Uploading files...</h4> <h4><i class="material-icons prefix">file_upload</i> Uploading files...</h4>
@ -77,4 +80,4 @@
<a href="#!" class="modal-close waves-effect waves-light btn red abort-request">Cancel</a> <a href="#!" class="modal-close waves-effect waves-light btn red abort-request">Cancel</a>
</div> </div>
</div> </div>
{% endblock %} {% endblock modals %}

View File

@ -107,7 +107,10 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock page_content %}
{% block modals %}
{{ super() }}
<div id="progress-modal" class="modal"> <div id="progress-modal" class="modal">
<div class="modal-content"> <div class="modal-content">
<h4><i class="material-icons prefix">file_upload</i> Uploading files...</h4> <h4><i class="material-icons prefix">file_upload</i> Uploading files...</h4>
@ -119,8 +122,7 @@
<a href="#!" class="modal-close waves-effect waves-light btn red abort-request">Cancel</a> <a href="#!" class="modal-close waves-effect waves-light btn red abort-request">Cancel</a>
</div> </div>
</div> </div>
{% endblock %} {% endblock modals %}
{% block scripts %} {% block scripts %}
{{ super() }} {{ super() }}
@ -132,4 +134,4 @@
window.location.href = url.toString(); window.location.href = url.toString();
}); });
</script> </script>
{% endblock %} {% endblock scripts %}

View File

@ -134,7 +134,10 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock page_content %}
{% block modals %}
{{ super() }}
<div id="progress-modal" class="modal"> <div id="progress-modal" class="modal">
<div class="modal-content"> <div class="modal-content">
<h4><i class="material-icons left">file_upload</i>Uploading files...</h4> <h4><i class="material-icons left">file_upload</i>Uploading files...</h4>
@ -146,8 +149,7 @@
<a href="#!" class="modal-close waves-effect waves-light btn red abort-request">Cancel</a> <a href="#!" class="modal-close waves-effect waves-light btn red abort-request">Cancel</a>
</div> </div>
</div> </div>
{% endblock %} {% endblock modals %}
{% block scripts %} {% block scripts %}
{{ super() }} {{ super() }}
@ -159,4 +161,4 @@
window.location.href = url.toString(); window.location.href = url.toString();
}); });
</script> </script>
{% endblock %} {% endblock scripts %}

View File

@ -64,9 +64,10 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock page_content %}
{% block modals %}
<!-- Modals --> {{ super() }}
<div class="modal" id="delete-account-modal"> <div class="modal" id="delete-account-modal">
<div class="modal-content"> <div class="modal-content">
<h4>Confirm deletion</h4> <h4>Confirm deletion</h4>
@ -77,4 +78,4 @@
<a href="{{ url_for('.delete') }}" class="btn red waves-effect waves-light"><i class="material-icons left">delete</i>Delete</a> <a href="{{ url_for('.delete') }}" class="btn red waves-effect waves-light"><i class="material-icons left">delete</i>Delete</a>
</div> </div>
</div> </div>
{% endblock page_content %} {% endblock modals %}