From 715c37813cd851ad309f6e82bdd4154ec716474f Mon Sep 17 00:00:00 2001 From: Patrick Jentsch Date: Thu, 30 Jul 2020 14:17:51 +0200 Subject: [PATCH] First RessourceList update --- daemon/logger/logger.py | 32 +- docker-compose.yml | 2 +- web/app/corpora/views.py | 6 +- web/app/static/js/nopaque.js | 2 - web/app/static/js/nopaque.lists.js | 599 +++++++++++------- web/app/templates/admin/index.html.j2 | 7 +- web/app/templates/corpora/corpus.html.j2 | 10 +- ...orpus_file.html.j2 => corpus_file.html.j2} | 0 web/app/templates/jobs/job.html.j2 | 10 +- web/app/templates/main/dashboard.html.j2 | 166 ++--- .../services/corpus_analysis.html.j2 | 68 +- web/config.py | 7 +- 12 files changed, 432 insertions(+), 477 deletions(-) rename web/app/templates/corpora/{edit_corpus_file.html.j2 => corpus_file.html.j2} (100%) diff --git a/daemon/logger/logger.py b/daemon/logger/logger.py index f347bc41..297ec964 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 6851a14e..e560ab8a 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 3f1bb8d8..2cbc3f6a 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('//files//edit', +@corpora.route('//files/', 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 f418eefa..2fe9a756 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 b0c10aae..556f686f 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 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: ` - - - book - - - -
- - - - - - - - - delete - - - edit - - - search - - - `, - 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: ` - - - - - - - edit - - - file_download - - - delete - - - `, - valueNames: ["filename", - "author", - "title", - "publishing_year", - {name: "edit-link", attr: "href"}, - {name: "download-link", attr: "href"}, - {name: "delete-modal", attr: "data-target"}]}, - Job: {item: ` - - - - - - -
- - - - - - - - delete - - - send - - - `, - 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 : ` - - - - file_download - - - `, - valueNames: ["filename", - "id", - {name: "download-link", attr: "href"}]}, - QueryResult: {item: ` - -
-
- - -
- - - - - delete - - - info - - - search - - - `, - 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: ` - - - - - - - - edit - - - `, - valueNames: ["username", - "email", - "role_id", - "confirmed", - "id", - {name: "profile-link", attr: "href"}]} + Corpus: { + item: ` + + + book + + + +
+ + + + + + + + + + `, + 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: ` + + + + + + + + + `, + 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: ` + + + + + + +
+ + + + + + + + + + `, + 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 : ` + + + + file_download + + + `, + valueNames: [ + "filename", + "id", + {name: "download-link", attr: "href"}, + ], + }, + QueryResult: { + item: ` + +
+
+ + +
+ + + + + + + `, + 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: ` + + + + + + + + + + `, + 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 acceb502..edc3f028 100644 --- a/web/app/templates/admin/index.html.j2 +++ b/web/app/templates/admin/index.html.j2 @@ -33,9 +33,10 @@ {% endblock %} diff --git a/web/app/templates/corpora/corpus.html.j2 b/web/app/templates/corpora/corpus.html.j2 index c153855d..b73a73ca 100644 --- a/web/app/templates/corpora/corpus.html.j2 +++ b/web/app/templates/corpora/corpus.html.j2 @@ -122,12 +122,6 @@ {% 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 6d2ab9d9..35154042 100644 --- a/web/app/templates/jobs/job.html.j2 +++ b/web/app/templates/jobs/job.html.j2 @@ -158,12 +158,6 @@ {% endblock %} diff --git a/web/app/templates/main/dashboard.html.j2 b/web/app/templates/main/dashboard.html.j2 index d0dbd242..d59eb21b 100644 --- a/web/app/templates/main/dashboard.html.j2 +++ b/web/app/templates/main/dashboard.html.j2 @@ -112,143 +112,49 @@

addNew job

- - - - - - - - - -