diff --git a/daemon/logger/logger.py b/daemon/logger/logger.py index f347bc413457025f6eced76cd1b05d646c8b02b6..297ec96476c7dd830b61de6ee588a16ed9628d1d 100644 --- a/daemon/logger/logger.py +++ b/daemon/logger/logger.py @@ -6,20 +6,24 @@ def init_logger(): ''' Functions initiates a logger instance. ''' - if not os.path.isfile('logs/nopaqued.log'): - file_path = os.path.join(os.getcwd(), 'logs/nopaqued.log') - log = open(file_path, 'w+') - log.close() - logging.basicConfig(datefmt='%Y-%m-%d %H:%M:%S', - filemode='w', filename='logs/nopaqued.log', - format='%(asctime)s - %(levelname)s - %(name)s - ' - '%(filename)s - %(lineno)d - %(message)s') - logger = logging.getLogger(__name__) - if os.environ.get('FLASK_CONFIG') == 'development': - logger.setLevel(logging.DEBUG) - if os.environ.get('FLASK_CONFIG') == 'production': - logger.setLevel(logging.WARNING) - return logger + os.makedirs('logs', exist_ok=True) + logging.basicConfig(filename='logs/nopaqued.log', + format='[%(asctime)s] %(levelname)s in ' + '%(pathname)s:%(lineno)d - %(message)s', + datefmt='%Y-%m-%d %H:%M:%S', filemode='w') + NOPAQUE_LOG_LEVEL = os.environ.get('NOPAQUE_LOG_LEVEL') + if NOPAQUE_LOG_LEVEL is None: + FLASK_CONFIG = os.environ.get('FLASK_CONFIG') + if FLASK_CONFIG == 'development': + logging.basicConfig(level='DEBUG') + elif FLASK_CONFIG == 'testing': + # TODO: Set an appropriate log level + pass + elif FLASK_CONFIG == 'production': + logging.basicConfig(level='ERROR') + else: + logging.basicConfig(level=NOPAQUE_LOG_LEVEL) + return logging.getLogger(__name__) if __name__ == '__main__': diff --git a/docker-compose.yml b/docker-compose.yml index 6851a14e5b693261cc847b6c02168abe9c912515..e560ab8a60c1ab63cf0dd1433950ec84e9365527 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -40,6 +40,6 @@ services: volumes: - "/srv/nopaque/db:/var/lib/postgresql/data" redis: - image: redis:5 + image: redis:6 volumes: - "redis-trash1:/data" diff --git a/web/app/corpora/views.py b/web/app/corpora/views.py index 3f1bb8d8bec472de7ebb9b582c1bcc50df0f5f4e..2cbc3f6a615d4e1f5693678e3ad7e8fcc967e1fe 100644 --- a/web/app/corpora/views.py +++ b/web/app/corpora/views.py @@ -169,10 +169,10 @@ def download_corpus_file(corpus_id, corpus_file_id): filename=corpus_file.filename) -@corpora.route('/<int:corpus_id>/files/<int:corpus_file_id>/edit', +@corpora.route('/<int:corpus_id>/files/<int:corpus_file_id>', methods=['GET', 'POST']) @login_required -def edit_corpus_file(corpus_id, corpus_file_id): +def corpus_file(corpus_id, corpus_file_id): corpus = Corpus.query.get_or_404(corpus_id) corpus_file = CorpusFile.query.get_or_404(corpus_file_id) if not corpus_file.corpus_id == corpus_id: @@ -212,7 +212,7 @@ def edit_corpus_file(corpus_id, corpus_file_id): edit_corpus_file_form.publishing_year.data = corpus_file.publishing_year edit_corpus_file_form.school.data = corpus_file.school edit_corpus_file_form.title.data = corpus_file.title - return render_template('corpora/edit_corpus_file.html.j2', + return render_template('corpora/corpus_file.html.j2', corpus_file=corpus_file, corpus=corpus, edit_corpus_file_form=edit_corpus_file_form, title='Edit corpus file') diff --git a/web/app/static/js/nopaque.js b/web/app/static/js/nopaque.js index f418eefae77132f82a4f89427dd5ae2e149b304a..2fe9a7568de1aab1474bfbcfa9d75d7dafa9ec53 100644 --- a/web/app/static/js/nopaque.js +++ b/web/app/static/js/nopaque.js @@ -43,7 +43,6 @@ nopaque.socket.init = function() { for (let subscriber of nopaque.queryResultsSubscribers) { subscriber._init(nopaque.user.query_results); } - RessourceList.modifyTooltips(false) }); nopaque.socket.on("user_data_stream_update", function(msg) { @@ -86,7 +85,6 @@ nopaque.socket.init = function() { for (let subscriber of nopaque.foreignQueryResultsSubscribers) { subscriber._init(nopaque.foreignUser.query_results); } - RessourceList.modifyTooltips(false) }); nopaque.socket.on("foreign_user_data_stream_update", function(msg) { diff --git a/web/app/static/js/nopaque.lists.js b/web/app/static/js/nopaque.lists.js index b0c10aaecf6b42f186815bb27720d4ce0c429957..556f686fe618cc734b373c37e068d173e349b871 100644 --- a/web/app/static/js/nopaque.lists.js +++ b/web/app/static/js/nopaque.lists.js @@ -1,27 +1,19 @@ class RessourceList extends List { - constructor(idOrElement, subscriberList, type, options={}) { - if (!["Corpus", "CorpusFile", "Job", "JobInput", "QueryResult", "User"].includes(type)) { - console.error("Unknown Type!"); - return; - } - if (subscriberList) { - super(idOrElement, {...RessourceList.options['common'], - ...RessourceList.options[type], - ...options}); - this.type = type; - subscriberList.push(this); - } else { - super(idOrElement, {...RessourceList.options['extended'], - ...RessourceList.options[type], - ...options}); - this.type = type; + constructor(idOrElement, subscriberList, type, options) { + if (!type || !["Corpus", "CorpusFile", "Job", "JobInput", "QueryResult", "User"].includes(type)) { + throw "Unknown Type!"; } + super(idOrElement, {...RessourceList.options['common'], + ...RessourceList.options[type], + ...(options ? options : {})}); + if (subscriberList) {subscriberList.push(this);} + this.type = type; } _init(ressources) { this.clear(); - this.addRessources(Object.values(ressources)); + this._add(Object.values(ressources)); this.sort("creation_date", {order: "desc"}); } @@ -35,7 +27,7 @@ class RessourceList extends List { switch(operation.op) { case "add": if (pathArray.includes("results")) {break;} - this.addRessources([operation.value]); + this._add([operation.value]); break; case "remove": this.remove("id", pathArray[0]); @@ -56,30 +48,17 @@ class RessourceList extends List { } } - addRessources(ressources) { - this.add(ressources.map(x => RessourceList.dataMapper[this.type](x))); - } - - // Use this to modify tooltips to show after 750ms. If loaded is set to - // true (default) tooltips will only be initialized if DOMContentLoaded event - // triggered. If you do not want to wait for this event set pass false. - static modifyTooltips(waitForDOMContentLoaded=true) { - if (waitForDOMContentLoaded) { - document.addEventListener('DOMContentLoaded', function() { - var elems = document.querySelectorAll('.tooltipped'); - var instances = M.Tooltip.init(elems, {enterDelay: 750}); - }); - } else { - var elems = document.querySelectorAll('.tooltipped'); - var instances = M.Tooltip.init(elems, {enterDelay: 750}); - } + _add(values, callback) { + this.add(values.map(x => RessourceList.dataMappers[this.type](x)), callback); + // Initialize modal and tooltipped elements in list + M.AutoInit(this.listContainer); } } -RessourceList.dataMapper = { +RessourceList.dataMappers = { // A data mapper describes entitys rendered per row. One key value pair holds // the data to be rendered in the list.js table. Key has to correspond // with the ValueNames defined below in RessourceList.options ValueNames. @@ -87,221 +66,361 @@ RessourceList.dataMapper = { // have to correspond with the class of an <a> element in the // RessourceList.options item blueprint. - // Mapping for Corpus entities shown in the dashboard table. - Corpus: corpus => ({creation_date: corpus.creation_date, - "delete-onclick": `prepareDeleteCorpusModal(${corpus.id})`, - description: corpus.description, - id: corpus.id, - "analyse-link": ["analysing", "prepared", "start analysis"].includes(corpus.status) ? `/corpora/${corpus.id}/analyse` : "", - "edit-link": `/corpora/${corpus.id}`, - status: corpus.status, - title: corpus.title}), - // Mapping for corpus file entities shown in the corpus overview - CorpusFile: corpus_file => ({filename: corpus_file.filename, - author: corpus_file.author, - title: corpus_file.title, - publishing_year: corpus_file.publishing_year, - "edit-link": `${corpus_file.corpus_id}/files/${corpus_file.id}/edit`, - "download-link": `${corpus_file.corpus_id}/files/${corpus_file.id}/download`, - "delete-modal": `delete-corpus-file-${corpus_file.id}-modal`}), - // Mapping for job entities shown in the dashboard table. - Job: job => ({creation_date: job.creation_date, - "delete-onclick": `prepareDeleteJobModal(${job.id})`, - description: job.description, - id: job.id, - link: `/jobs/${job.id}`, - service: job.service, - status: job.status, - title: job.title}), - // Mapping for job input files shown in table on every job page - JobInput: job_input => ({filename: job_input.filename, - id: job_input.job_id, - "download-link": `${job_input.job_id}/inputs/${job_input.id}/download`}), - // Mapping for imported result entities from corpus analysis. - // Shown in imported results table - QueryResult: query_result => ({corpus_name: query_result.query_metadata.corpus_name, - "delete-onclick": `prepareDeleteQueryResultModal(${query_result.id})`, - description: query_result.description, - id: query_result.id, - "inspect-link": `/query_results/${query_result.id}/inspect`, - link: `/query_results/${query_result.id}`, - query: query_result.query_metadata.query, - title: query_result.title}), - // Mapping for user entities shown in admin table - User: user => ({username: user.username, - email: user.email, - role_id: user.role_id, - confirmed: user.confirmed, - id: user.id, - "profile-link": `user/${user.id}`}) + /* ### Corpus mapper ### */ + Corpus: corpus => ({ + creation_date: corpus.creation_date, + description: corpus.description, + id: corpus.id, + link: `/corpora/${corpus.id}`, + status: corpus.status, + title: corpus.title, + title1: corpus.title, + "analyse-link": ["analysing", "prepared", "start analysis"].includes(corpus.status) ? `/corpora/${corpus.id}/analyse` : "", + "delete-link": `/corpora/${corpus.id}/delete`, + "delete-modal": `delete-corpus-${corpus.id}-modal`, + "delete-modal-trigger": `delete-corpus-${corpus.id}-modal`, + }), + /* ### CorpusFile mapper ### TODO: replace delete-modal with delete-onclick */ + CorpusFile: corpus_file => ({ + author: corpus_file.author, + filename: corpus_file.filename, + link: `${corpus_file.corpus_id}/files/${corpus_file.id}`, + publishing_year: corpus_file.publishing_year, + title: corpus_file.title, + title1: corpus_file.title, + "delete-link": `/corpora/${corpus_file.corpus_id}/files/${corpus_file.id}/delete`, + "delete-modal": `delete-corpus-file-${corpus_file.id}-modal`, + "delete-modal-trigger": `delete-corpus-file-${corpus_file.id}-modal`, + "download-link": `${corpus_file.corpus_id}/files/${corpus_file.id}/download`, + }), + /* ### Job mapper ### */ + Job: job => ({ + creation_date: job.creation_date, + description: job.description, + id: job.id, + link: `/jobs/${job.id}`, + service: job.service, + status: job.status, + title: job.title, + title1: job.title, + "delete-link": `/jobs/${job.id}/delete`, + "delete-modal": `delete-job-${job.id}-modal`, + "delete-modal-trigger": `delete-job-${job.id}-modal`, + }), + /* ### JobInput mapper ### */ + JobInput: job_input => ({ + filename: job_input.filename, + id: job_input.job_id, + "download-link": `${job_input.job_id}/inputs/${job_input.id}/download` + }), + /* ### QueryResult mapper ### */ + QueryResult: query_result => ({ + corpus_name: query_result.query_metadata.corpus_name, + description: query_result.description, + id: query_result.id, + link: `/query_results/${query_result.id}`, + query: query_result.query_metadata.query, + title: query_result.title, + "delete-link": `/query_results/${query_result.id}/delete`, + "delete-modal": `delete-query-result-${query_result.id}-modal`, + "delete-modal-trigger": `delete-query-result-${query_result.id}-modal`, + "inspect-link": `/query_results/${query_result.id}/inspect`, + }), + /* ### User mapper ### */ + User: user => ({ + confirmed: user.confirmed, + email: user.email, + id: user.id, + link: `user/${user.id}`, + role_id: user.role_id, + username: user.username, + username2: user.username, + "delete-link": `/admin/user/${user.id}/delete`, + "delete-modal": `delete-user-${user.id}-modal`, + "delete-modal-trigger": `delete-user-${user.id}-modal`, + }), }; RessourceList.options = { // common list.js options for 4 rows per page etc. - common: {page: 4, pagination: {innerWindow: 8, outerWindow: 1}}, + common: { + page: 4, + pagination: { + innerWindow: 8, + outerWindow: 1, + }, + }, // extended list.js options for 10 rows per page etc. - extended: {page: 10, - pagination: [{name: "paginationTop", - paginationClass: "paginationTop", - innerWindow: 8, - outerWindow: 1}, - {paginationClass: "paginationBottom", - innerWindow: 8, - outerWindow: 1}]}, + extended: { + page: 10, + pagination: [ + { + name: "paginationTop", + paginationClass: "paginationTop", + innerWindow: 8, + outerWindow: 1 + }, + { + paginationClass: "paginationBottom", + innerWindow: 8, + outerWindow: 1, + }, + ], + }, /* Type specific List.js options. Usually only "item" and "valueNames" gets * defined here but it is possible to define other List.js options. * item: https://listjs.com/api/#item * valueNames: https://listjs.com/api/#valueNames */ - Corpus: {item: `<tr> - <td> - <a class="btn-floating disabled"> - <i class="material-icons service">book</i> - </a> - </td> - <td> - <b class="title"></b><br> - <i class="description"></i> - </td> - <td> - <span class="badge new status" data-badge-caption=""> - </span> - </td> - <td class="actions right-align"> - <a class="btn-floating modal-trigger red tooltipped waves-effect waves-light delete-onclick" data-position="top" data-tooltip="Delete"> - <i class="material-icons">delete</i> - </a> - <a class="btn-floating tooltipped waves-effect waves-light edit-link" data-position="top" data-tooltip="Edit"> - <i class="material-icons">edit</i> - </a> - <a class="btn-floating tooltipped waves-effect waves-light analyse-link" data-position="top" data-tooltip="Analyse"> - <i class="material-icons">search</i> - </a> - </td> - </tr>`, - valueNames: ["creation_date", - "description", - "title", - {data: ["id"]}, - {name: "analyse-link", attr: "href"}, - {name: "delete-onclick", attr: "onclick"}, - {name: "edit-link", attr: "href"}, - {name: "status", attr: "data-status"}]}, - CorpusFile: {item: `<tr> - <td class="filename" style="word-break: break-word;"></td> - <td class="author" style="word-break: break-word;"></td> - <td class="title" style="word-break: break-word;"></td> - <td class="publishing_year" style="word-break: break-word;"></td> - <td class="actions right-align"> - <a class="btn-floating tooltipped waves-effect waves-light edit-link" data-position="top" data-tooltip="Edit"> - <i class="material-icons">edit</i> - </a> - <a class="btn-floating tooltipped waves-effect waves-light download-link" data-position="top" data-tooltip="Download"> - <i class="material-icons">file_download</i> - </a> - <a class="btn-floating modal-trigger red tooltipped waves-effect waves-light delete-modal" data-position="top" data-tooltip="Delete"> - <i class="material-icons">delete</i> - </a> - </td> - </tr>`, - valueNames: ["filename", - "author", - "title", - "publishing_year", - {name: "edit-link", attr: "href"}, - {name: "download-link", attr: "href"}, - {name: "delete-modal", attr: "data-target"}]}, - Job: {item: `<tr> - <td> - <a class="btn-floating disabled"> - <i class="material-icons service"></i> - </a> - </td> - <td> - <b class="title"></b><br> - <i class="description"></i> - </td> - <td> - <span class="badge new status" data-badge-caption=""></span> - </td> - <td class="actions right-align"> - <a class="btn-floating modal-trigger red tooltipped waves-effect waves-light delete-onclick" data-position="top" data-tooltip="Delete"> - <i class="material-icons">delete</i> - </a> - <a class="btn-floating tooltipped waves-effect waves-light link" data-position="top" data-tooltip="Go to Job"> - <i class="material-icons">send</i> - </a> - </td> - </tr>`, - valueNames: ["creation_date", - "description", - "title", - {data: ["id"]}, - {name: "delete-onclick", attr: "onclick"}, - {name: "link", attr: "href"}, - {name: "service", attr: "data-service"}, - {name: "status", attr: "data-status"}]}, - JobInput: {item : `<tr> - <td class="filename"></td> - <td class="actions right-align"> - <a class="btn-floating tooltipped waves-effect waves-light download-link" data-position="top" data-tooltip="Download"> - <i class="material-icons">file_download</i> - </a> - </td> - </tr>`, - valueNames: ["filename", - "id", - {name: "download-link", attr: "href"}]}, - QueryResult: {item: `<tr> - <td> - <b class="title"></b><br> - <i class="description"></i><br> - </td> - <td> - <span class="corpus_name"></span><br> - <span class="query"></span> - </td> - <td class="actions right-align"> - <a class="btn-floating modal-trigger red tooltipped waves-effect waves-light delete-onclick" data-position="top" data-tooltip="Delete"> - <i class="material-icons">delete</i> - </a> - <a class="btn-floating tooltipped link waves-effect waves-light" data-position="top" data-tooltip="Info"> - <i class="material-icons">info</i> - </a> - <a class="btn-floating tooltipped waves-effect waves-light inspect-link" data-position="top" data-tooltip="Analyse"> - <i class="material-icons">search</i> - </a> - </td> - </tr>`, - valueNames: ["corpus_name", - "description", - "query", - "title", - {data: ["id"]}, - {name: "delete-onclick", attr: "onclick"}, - {name: "inspect-link", attr: "href"}, - {name: "link", attr: "href"}]}, - // User entity blueprint setting html strucuture per entity per row - // Link classes have to correspond with Links defined in the Mapping process - User: {item: `<tr> - <td class="username"></td> - <td class="email"></td> - <td class="role_id"></td> - <td class="confirmed"></td> - <td class="id"></td> - <td class="actions right-align"> - <a class="btn-floating tooltipped profile-link waves-effect waves-light" data-position="top" data-tooltip="Edit User"> - <i class="material-icons">edit</i> - </a> - </td> - </tr>`, - valueNames: ["username", - "email", - "role_id", - "confirmed", - "id", - {name: "profile-link", attr: "href"}]} + Corpus: { + item: `<tr> + <td> + <a class="btn-floating disabled"> + <i class="material-icons service">book</i> + </a> + </td> + <td> + <b class="title"></b><br> + <i class="description"></i> + </td> + <td> + <span class="badge new status" data-badge-caption=""></span> + </td> + <td> + <div class="right-align"> + <a class="btn-floating modal-trigger red tooltipped waves-effect waves-light delete-modal-trigger" data-position="top" data-tooltip="Delete"> + <i class="material-icons">delete</i> + </a> + <a class="btn-floating tooltipped waves-effect waves-light link" data-position="top" data-tooltip="Edit"> + <i class="material-icons">edit</i> + </a> + <a class="btn-floating tooltipped waves-effect waves-light analyse-link" data-position="top" data-tooltip="Analyse"> + <i class="material-icons">search</i> + </a> + </div> + <div class="modal delete-modal"> + <div class="modal-content"> + <h4>Confirm corpus deletion</h4> + <p>Do you really want to delete the corpus <b class="title1"></b>? All 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 delete-link"><i class="material-icons left">delete</i>Delete</a> + </div> + </div> + </td> + </tr>`, + valueNames: [ + "creation_date", + "description", + "title", + "title1", + {data: ["id"]}, + {name: "analyse-link", attr: "href"}, + {name: "delete-link", attr: "href"}, + {name: "delete-modal-trigger", attr: "data-target"}, + {name: "delete-modal", attr: "id"}, + {name: "link", attr: "href"}, + {name: "status", attr: "data-status"}, + ] + }, + CorpusFile: { + item: `<tr> + <td class="filename" style="word-break: break-word;"></td> + <td class="author" style="word-break: break-word;"></td> + <td class="title" style="word-break: break-word;"></td> + <td class="publishing_year" style="word-break: break-word;"></td> + <td> + <div class="right-align"> + <a class="btn-floating modal-trigger red tooltipped waves-effect waves-light delete-modal-trigger" data-position="top" data-tooltip="Delete"> + <i class="material-icons">delete</i> + </a> + <a class="btn-floating tooltipped waves-effect waves-light download-link" data-position="top" data-tooltip="Download"> + <i class="material-icons">file_download</i> + </a> + <a class="btn-floating tooltipped waves-effect waves-light link" data-position="top" data-tooltip="Edit"> + <i class="material-icons">edit</i> + </a> + </div> + <div class="modal delete-modal"> + <div class="modal-content"> + <h4>Confirm corpus file deletion</h4> + <p>Do you really want to delete the corpus file <b class="title1"></b>? It 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 delete-link"><i class="material-icons left">delete</i>Delete</a> + </div> + </div> + </td> + </tr>`, + valueNames: [ + "author", + "filename", + "publishing_year", + "title", + "title1", + {name: "delete-link", attr: "href"}, + {name: "delete-modal-trigger", attr: "data-target"}, + {name: "delete-modal", attr: "id"}, + {name: "download-link", attr: "href"}, + {name: "link", attr: "href"}, + ], + }, + Job: { + item: `<tr> + <td> + <a class="btn-floating disabled"> + <i class="material-icons service"></i> + </a> + </td> + <td> + <b class="title"></b><br> + <i class="description"></i> + </td> + <td> + <span class="badge new status" data-badge-caption=""></span> + </td> + <td> + <div class="right-align"> + <a class="btn-floating modal-trigger red tooltipped waves-effect waves-light delete-modal-trigger" data-position="top" data-tooltip="Delete"> + <i class="material-icons">delete</i> + </a> + <a class="btn-floating tooltipped waves-effect waves-light link" data-position="top" data-tooltip="Go to job"> + <i class="material-icons">send</i> + </a> + </div> + <div class="modal delete-modal"> + <div class="modal-content"> + <h4>Confirm job deletion</h4> + <p>Do you really want to delete the job <b class="title1"></b>? All 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 delete-link"><i class="material-icons left">delete</i>Delete</a> + </div> + </div> + </td> + </tr>`, + valueNames: [ + "creation_date", + "description", + "title", + "title1", + {data: ["id"]}, + {name: "delete-link", attr: "href"}, + {name: "delete-modal-trigger", attr: "data-target"}, + {name: "delete-modal", attr: "id"}, + {name: "link", attr: "href"}, + {name: "service", attr: "data-service"}, + {name: "status", attr: "data-status"}, + ], + }, + JobInput: { + item : `<tr> + <td class="filename"></td> + <td class="right-align"> + <a class="btn-floating tooltipped waves-effect waves-light download-link" data-position="top" data-tooltip="Download"> + <i class="material-icons">file_download</i> + </a> + </td> + </tr>`, + valueNames: [ + "filename", + "id", + {name: "download-link", attr: "href"}, + ], + }, + QueryResult: { + item: `<tr> + <td> + <b class="title"></b><br> + <i class="description"></i><br> + </td> + <td> + <span class="corpus_name"></span><br> + <span class="query"></span> + </td> + <td> + <div class="right-align"> + <a class="btn-floating modal-trigger red tooltipped waves-effect waves-light delete-modal-trigger" data-position="top" data-tooltip="Delete"> + <i class="material-icons">delete</i> + </a> + <a class="btn-floating tooltipped waves-effect waves-light link" data-position="top" data-tooltip="Info"> + <i class="material-icons">info</i> + </a> + <a class="btn-floating tooltipped waves-effect waves-light inspect-link" data-position="top" data-tooltip="Analyse"> + <i class="material-icons">search</i> + </a> + </div> + <div class="modal delete-modal"> + <div class="modal-content"> + <h4>Confirm query result deletion</h4> + <p>Do you really want to delete the query result <b class="title1"></b>? It 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 delete-link"><i class="material-icons left">delete</i>Delete</a> + </div> + </div> + </td> + </tr>`, + valueNames: [ + "corpus_name", + "description", + "query", + "title", + "title2", + {data: ["id"]}, + {name: "delete-link", attr: "href"}, + {name: "delete-modal-trigger", attr: "data-target"}, + {name: "delete-modal", attr: "id"}, + {name: "inspect-link", attr: "href"}, + {name: "link", attr: "href"}, + ], + }, + User: { + item: `<tr> + <td class="username"></td> + <td class="email"></td> + <td class="role_id"></td> + <td class="confirmed"></td> + <td class="id"></td> + <td> + <div class="right-align"> + <a class="btn-floating modal-trigger red tooltipped waves-effect waves-light delete-modal-trigger" data-position="top" data-tooltip="Delete"> + <i class="material-icons">delete</i> + </a> + <a class="btn-floating tooltipped waves-effect waves-light link" data-position="top" data-tooltip="Go to user"> + <i class="material-icons">send</i> + </a> + </div> + <div class="modal delete-modal"> + <div class="modal-content"> + <h4>Confirm corpus deletion</h4> + <p>Do you really want to delete the job <b class="title1"></b>? All 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 delete-link"><i class="material-icons left">delete</i>Delete</a> + </div> + </div> + </td> + </tr>`, + valueNames: [ + "username", + "username2", + "email", + "role_id", + "confirmed", + "id", + {name: "link", attr: "href"}, + {name: "delete-link", attr: "href"}, + {name: "delete-modal-trigger", attr: "data-target"}, + {name: "delete-modal", attr: "id"}, + ], + }, }; diff --git a/web/app/templates/admin/index.html.j2 b/web/app/templates/admin/index.html.j2 index acceb502b4a6c169452baab3b77b2391e1edd54c..edc3f028d85476b5688786f0be23e0e2885a25be 100644 --- a/web/app/templates/admin/index.html.j2 +++ b/web/app/templates/admin/index.html.j2 @@ -33,9 +33,10 @@ </div> <script> - var ressources = {{ users|tojson|safe }}; + var users = {{ users|tojson|safe }}; var userList = new RessourceList('users', null, "User"); - userList.addRessources(ressources); - RessourceList.modifyTooltips(); + document.addEventListener("DOMContentLoaded", () => { + userList.addRessources(users); + }); </script> {% endblock %} diff --git a/web/app/templates/corpora/corpus.html.j2 b/web/app/templates/corpora/corpus.html.j2 index c153855dd5645d84b34c4f9731c67eb6e81c146a..b73a73caf945ff8e43ae947b6132ef71f5d9e52e 100644 --- a/web/app/templates/corpora/corpus.html.j2 +++ b/web/app/templates/corpora/corpus.html.j2 @@ -122,12 +122,6 @@ <script> - // create corpus file table - var ressources = {{ corpus_files|tojson|safe }}; - var corpusFilesList = new RessourceList("corpus-files", null, "CorpusFile"); - corpusFilesList.addRessources(ressources); - RessourceList.modifyTooltips(); - class InformationUpdater { constructor(corpusId, foreignCorpusFlag) { this.corpusId = corpusId; @@ -215,5 +209,9 @@ nopaque.socket.emit("foreign_user_data_stream_init", {{ corpus.user_id }}); }); {% endif %} + var corpusFilesList = new RessourceList("corpus-files", null, "CorpusFile"); + document.addEventListener("DOMContentLoaded", () => { + corpusFilesList._add({{ corpus_files|tojson|safe }}); + }); </script> {% endblock %} diff --git a/web/app/templates/corpora/edit_corpus_file.html.j2 b/web/app/templates/corpora/corpus_file.html.j2 similarity index 100% rename from web/app/templates/corpora/edit_corpus_file.html.j2 rename to web/app/templates/corpora/corpus_file.html.j2 diff --git a/web/app/templates/jobs/job.html.j2 b/web/app/templates/jobs/job.html.j2 index 6d2ab9d9516a4d0eb493ec0229d44a345cb21f7c..35154042aac8f8d79a0a85a2214e196573b39040 100644 --- a/web/app/templates/jobs/job.html.j2 +++ b/web/app/templates/jobs/job.html.j2 @@ -158,12 +158,6 @@ <script> -// job_input_table code - var ressources = {{ job_inputs|tojson|safe }}; - var jobInputsList = new RessourceList("inputs", null, "JobInput"); - jobInputsList.addRessources(ressources); - RessourceList.modifyTooltips(); - class InformationUpdater { constructor(jobId, foreignJobFlag) { this.jobId = jobId; @@ -299,5 +293,9 @@ nopaque.socket.emit("foreign_user_data_stream_init", {{ job.user_id }}); }); {% endif %} + let jobInputsList = new RessourceList("inputs", null, "JobInput"); + document.addEventListener("DOMContentLoaded", () => { + jobInputsList._add({{ job_inputs|tojson|safe }}); + }); </script> {% endblock %} diff --git a/web/app/templates/main/dashboard.html.j2 b/web/app/templates/main/dashboard.html.j2 index d0dbd242838467313d25518be7ba6e4715b3dd32..d59eb21b2cc0528f5d76e5da9b66a0f47c86beaf 100644 --- a/web/app/templates/main/dashboard.html.j2 +++ b/web/app/templates/main/dashboard.html.j2 @@ -112,143 +112,49 @@ <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> - -<!-- Modals --> -<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 <b id="selected-corpus-title"></b>? All 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" id="selected-corpus-delete-link"><i class="material-icons left">delete</i>Delete</a> - </div> -</div> - -<div id="delete-job-modal" class="modal"> - <div class="modal-content"> - <h4>Confirm job deletion</h4> - <p>Do you really want to delete the job <b id="selected-job-title"></b>? All 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" id="selected-job-delete-link"><i class="material-icons left">delete</i>Delete</a> - </div> -</div> - -<div id="delete-query-result-modal" class="modal no-autoinit"> - <div class="modal-content"> - <h4>Confirm query result deletion</h4> - <p>Do you really want to delete the query result <b id="selected-query-result-title"></b>? It 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" id="selected-query-result-delete-link"><i class="material-icons left">delete</i>Delete</a> - </div> -</div> - -<div id="new-job-modal" class="modal"> - <div class="modal-content"> - <h4>Select a service</h4> - <div class="row"> - <div class="col s12 m4"> - <a href="{{ url_for('services.service', service='file-setup') }}" style="color: rgba(0,0,0,0.87);"> - <div class="card-panel center-align hoverable"> - <i class="large material-icons" style="color: #ee6e73;">burst_mode</i> - <p>File setup</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> - </div> - </a> - </div> - <div class="col s12 m4"> - <a href="{{ url_for('services.service', service='ocr') }}" style="color: rgba(0,0,0,0.87);"> - <div class="card-panel center-align hoverable"> - <i class="large material-icons" style="color: #ee6e73;">find_in_page</i> - <p>Optical Character Recognition</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> - </div> - </a> - </div> - <div class="col s12 m4"> - <a href="{{ url_for('services.service', service='nlp') }}" style="color: rgba(0,0,0,0.87);"> - <div class="card-panel center-align hoverable"> - <i class="large material-icons" style="color: #ee6e73;">format_textdirection_l_to_r</i> - <p>Natural Language Processing</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> - </div> - </a> + <div id="new-job-modal" class="modal"> + <div class="modal-content"> + <h4>Select a service</h4> + <div class="row"> + <div class="col s12 m4"> + <a href="{{ url_for('services.service', service='file-setup') }}" style="color: rgba(0,0,0,0.87);"> + <div class="card-panel center-align hoverable"> + <i class="large material-icons" style="color: #ee6e73;">burst_mode</i> + <p>File setup</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> + </div> + </a> + </div> + <div class="col s12 m4"> + <a href="{{ url_for('services.service', service='ocr') }}" style="color: rgba(0,0,0,0.87);"> + <div class="card-panel center-align hoverable"> + <i class="large material-icons" style="color: #ee6e73;">find_in_page</i> + <p>Optical Character Recognition</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> + </div> + </a> + </div> + <div class="col s12 m4"> + <a href="{{ url_for('services.service', service='nlp') }}" style="color: rgba(0,0,0,0.87);"> + <div class="card-panel center-align hoverable"> + <i class="large material-icons" style="color: #ee6e73;">format_textdirection_l_to_r</i> + <p>Natural Language Processing</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> + </div> + </a> + </div> </div> </div> - </div> - <div class="modal-footer"> - <a href="#!" class="modal-close waves-effect waves-light btn-flat">Close</a> + <div class="modal-footer"> + <a href="#!" class="modal-close waves-effect waves-light btn-flat">Close</a> + </div> </div> </div> <script> - var corpusList = new RessourceList("corpora", nopaque.corporaSubscribers, - "Corpus"); + var corpusList = new RessourceList("corpora", nopaque.corporaSubscribers, "Corpus"); var jobList = new RessourceList("jobs", nopaque.jobsSubscribers, "Job"); - var queryResultList = new RessourceList("query-results", - nopaque.queryResultsSubscribers, - "QueryResult", {page: 10}); - var deleteCorpusModalElement = document.getElementById("delete-corpus-modal"); - var deleteCorpusModal; - var deleteJobModalElement = document.getElementById("delete-job-modal"); - var deleteJobModal; - var deleteQueryResultModalElement = document.getElementById("delete-query-result-modal"); - var deleteQueryResultModal; - var selectedCorpusDeleteLinkElement = document.getElementById("selected-corpus-delete-link"); - var selectedCorpusTitleElement = document.getElementById("selected-corpus-title"); - var selectedJobDeleteLinkElement = document.getElementById("selected-job-delete-link"); - var selectedJobTitleElement = document.getElementById("selected-job-title"); - var selectedQueryResultDeleteLinkElement = document.getElementById("selected-query-result-delete-link"); - var selectedQueryResultTitleElement = document.getElementById("selected-query-result-title"); - function prepareDeleteCorpusModal(selectedCorpusId) { - var selectedCorpus; - if (selectedCorpusId in nopaque.user.corpora) { - selectedCorpus = nopaque.user.corpora[selectedCorpusId]; - selectedCorpusDeleteLinkElement.href = `/corpora/${selectedCorpus.id}/delete`; - selectedCorpusTitleElement.innerText = selectedCorpus.title; - } else { - selectedQueryResult = undefined; - selectedCorpusDeleteLinkElement.href = ""; - selectedCorpusTitleElement.innerText = ""; - } - deleteCorpusModal.open(); - } - function prepareDeleteJobModal(selectedJobId) { - var selectedJob; - if (selectedJobId in nopaque.user.jobs) { - selectedJob = nopaque.user.jobs[selectedJobId]; - selectedJobDeleteLinkElement.href = `/jobs/${selectedJob.id}/delete`; - selectedJobTitleElement.innerText = selectedJob.title; - } else { - selectedJob = undefined; - selectedJobDeleteLinkElement.href = ""; - selectedJobTitleElement.innerText = ""; - } - deleteJobModal.open(); - } - function prepareDeleteQueryResultModal(selectedQueryResultId) { - var selectedQueryResult; - if (selectedQueryResultId in nopaque.user.query_results) { - selectedQueryResult = nopaque.user.query_results[selectedQueryResultId]; - selectedQueryResultDeleteLinkElement.href = `/query_results/${selectedQueryResult.id}/delete`; - selectedQueryResultTitleElement.innerText = selectedQueryResult.title; - } else { - selectedQueryResult = undefined; - selectedQueryResultDeleteLinkElement.href = ""; - selectedQueryResultTitleElement.innerText = ""; - } - deleteQueryResultModal.open(); - } - document.addEventListener("DOMContentLoaded", () => { - deleteCorpusModal = M.Modal.init(deleteCorpusModalElement); - deleteJobModal = M.Modal.init(deleteJobModalElement); - deleteQueryResultModal = M.Modal.init(deleteQueryResultModalElement); - }); + var queryResultList = new RessourceList("query-results", nopaque.queryResultsSubscribers, "QueryResult"); </script> {% endblock %} diff --git a/web/app/templates/services/corpus_analysis.html.j2 b/web/app/templates/services/corpus_analysis.html.j2 index 2067e14c87c5fe33dd9df888b7c1e0d992eb9fc3..53251cffc84b9fa5747f477d69fc34334a1cd00b 100644 --- a/web/app/templates/services/corpus_analysis.html.j2 +++ b/web/app/templates/services/corpus_analysis.html.j2 @@ -83,72 +83,8 @@ </div> </div> -<!-- Modals --> -<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 <b id="selected-corpus-title"></b>? All 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" id="selected-corpus-delete-link"><i class="material-icons left">delete</i>Delete</a> - </div> -</div> - -<div id="delete-query-result-modal" class="modal no-autoinit"> - <div class="modal-content"> - <h4>Confirm query result deletion</h4> - <p>Do you really want to delete the query result <b id="selected-query-result-title"></b>? It 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" id="selected-query-result-delete-link"><i class="material-icons left">delete</i>Delete</a> - </div> -</div> - <script> - var corpusList = new RessourceList("corpora", nopaque.corporaSubscribers, - "Corpus", {page: 10}); - var queryResultList = new RessourceList("query-results", - nopaque.queryResultsSubscribers, - "QueryResult", {page: 10}); - var deleteCorpusModalElement = document.getElementById("delete-corpus-modal"); - var deleteCorpusModal; - var deleteQueryResultModalElement = document.getElementById("delete-query-result-modal"); - var deleteQueryResultModal; - var selectedCorpusDeleteLinkElement = document.getElementById("selected-corpus-delete-link"); - var selectedCorpusTitleElement = document.getElementById("selected-corpus-title"); - var selectedQueryResultDeleteLinkElement = document.getElementById("selected-query-result-delete-link"); - var selectedQueryResultTitleElement = document.getElementById("selected-query-result-title"); - function prepareDeleteCorpusModal(selectedCorpusId) { - var selectedCorpus; - if (selectedCorpusId in nopaque.user.corpora) { - selectedCorpus = nopaque.user.corpora[selectedCorpusId]; - selectedCorpusDeleteLinkElement.href = `/corpora/${selectedCorpus.id}/delete`; - selectedCorpusTitleElement.innerText = selectedCorpus.title; - } else { - selectedQueryResult = undefined; - selectedCorpusDeleteLinkElement.href = ""; - selectedCorpusTitleElement.innerText = ""; - } - deleteCorpusModal.open(); - } - function prepareDeleteQueryResultModal(selectedQueryResultId) { - var selectedQueryResult; - if (selectedQueryResultId in nopaque.user.query_results) { - selectedQueryResult = nopaque.user.query_results[selectedQueryResultId]; - selectedQueryResultDeleteLinkElement.href = `/query_results/${selectedQueryResult.id}/delete`; - selectedQueryResultTitleElement.innerText = selectedQueryResult.title; - } else { - selectedQueryResult = undefined; - selectedQueryResultDeleteLinkElement.href = ""; - selectedQueryResultTitleElement.innerText = ""; - } - deleteQueryResultModal.open(); - } - document.addEventListener("DOMContentLoaded", () => { - deleteCorpusModal = M.Modal.init(deleteCorpusModalElement); - deleteQueryResultModal = M.Modal.init(deleteQueryResultModalElement); - }); + var corpusList = new RessourceList("corpora", nopaque.corporaSubscribers, "Corpus"); + var queryResultList = new RessourceList("query-results", nopaque.queryResultsSubscribers, "QueryResult"); </script> {% endblock %} diff --git a/web/config.py b/web/config.py index 6c2143ad5953e3bc28858202d08d65c6fe984592..7bb300ffe76ce8be5600c09a8e0552e968995b21 100644 --- a/web/config.py +++ b/web/config.py @@ -47,12 +47,7 @@ class Config: @staticmethod def init_app(app): - proxy_fix_kwargs = { - 'x_for': 1, - 'x_host': 1, - 'x_port': 1, - 'x_proto': 1, - } + proxy_fix_kwargs = {'x_for': 1, 'x_host': 1, 'x_port': 1, 'x_proto': 1} app.wsgi_app = ProxyFix(app.wsgi_app, **proxy_fix_kwargs)