diff --git a/app/static/js/ResourceLists/CorpusFileList.js b/app/static/js/ResourceLists/CorpusFileList.js new file mode 100644 index 00000000..4549c503 --- /dev/null +++ b/app/static/js/ResourceLists/CorpusFileList.js @@ -0,0 +1,156 @@ +class CorpusFileList extends ResourceList { + static autoInit() { + for (let corpusFileListElement of document.querySelectorAll('.corpus-file-list:not(.no-autoinit)')) { + new CorpusFileList(corpusFileListElement); + } + } + + constructor(listContainerElement, options = {}) { + super(listContainerElement, options); + this.listjs.list.addEventListener('click', (event) => {this.onClick(event)}); + this.isInitialized = false; + this.userId = listContainerElement.dataset.userId; + this.corpusId = listContainerElement.dataset.corpusId; + app.subscribeUser(this.userId).then((response) => { + app.socket.on('PATCH', (patch) => { + if (this.isInitialized) {this.onPatch(patch);} + }); + }); + app.getUser(this.userId).then((user) => { + this.add(Object.values(user.corpora[this.corpusId].files)); + this.isInitialized = true; + }); + } + + // #region Mandatory getters and methods to implement + get item() { + return ` + + + + + + + delete + file_download + send + + + `.trim(); + } + + get valueNames() { + return [ + {data: ['id']}, + {data: ['creation-date']}, + 'author', + 'filename', + 'publishing-year', + 'title' + ]; + } + + initListContainerElement() { + if (!this.listContainerElement.hasAttribute('id')) { + this.listContainerElement.id = Utils.generateElementId('corpus-file-list-'); + } + let listSearchElementId = Utils.generateElementId(`${this.listContainerElement.id}-search-`); + this.listContainerElement.innerHTML = ` +
+ search + + +
+ + + + + + + + + + + +
FilenameAuthorTitlePublishing year
+ + `.trim(); + } + // #endregion + + // #region Optional methods to implement + mapResourceToValue(corpusFile) { + return { + 'id': corpusFile.id, + 'author': corpusFile.author, + 'creation-date': corpusFile.creation_date, + 'filename': corpusFile.filename, + 'publishing-year': corpusFile.publishing_year, + 'title': corpusFile.title + }; + } + + sort() { + this.listjs.sort('creation-date', {order: 'desc'}); + } + // #endregion + + onClick(event) { + let corpusFileElement = event.target.closest('tr'); + if (corpusFileElement === null) {return;} + let corpusFileId = corpusFileElement.dataset.id; + if (corpusFileId === undefined) {return;} + let actionButtonElement = event.target.closest('.action-button'); + let action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action; + switch (action) { + case 'delete': { + Utils.deleteCorpusFileRequest(this.userId, this.corpusId, corpusFileId); + break; + } + case 'download': { + window.location.href = `/corpora/${this.corpusId}/files/${corpusFileId}/download`; + break; + } + case 'view': { + window.location.href = `/corpora/${this.corpusId}/files/${corpusFileId}`; + break; + } + default: { + break; + } + } + } + + onPatch(patch) { + let re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/files/([A-Za-z0-9]*)`); + let filteredPatch = patch.filter(operation => re.test(operation.path)); + for (let operation of filteredPatch) { + switch(operation.op) { + case 'add': { + let re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/files/([A-Za-z0-9]*)$`); + if (re.test(operation.path)) {this.add(operation.value);} + break; + } + case 'remove': { + let re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/files/([A-Za-z0-9]*)$`); + if (re.test(operation.path)) { + let [match, corpusFileId] = operation.path.match(re); + this.remove(corpusFileId); + } + break; + } + case 'replace': { + let re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/files/([A-Za-z0-9]*)/(author|filename|publishing_year|title)$`); + if (re.test(operation.path)) { + let [match, corpusFileId, valueName] = operation.path.match(re); + this.replace(corpusFileId, valueName.replace('_', '-'), operation.value); + } + break; + } + default: { + break; + } + } + } + } +} diff --git a/app/static/js/ResourceLists/CorpusList.js b/app/static/js/ResourceLists/CorpusList.js new file mode 100644 index 00000000..1c7ccb81 --- /dev/null +++ b/app/static/js/ResourceLists/CorpusList.js @@ -0,0 +1,146 @@ +class CorpusList extends ResourceList { + static autoInit() { + for (let corpusListElement of document.querySelectorAll('.corpus-list:not(.no-autoinit)')) { + new CorpusList(corpusListElement); + } + } + + constructor(listContainerElement, options = {}) { + super(listContainerElement, options); + this.listjs.list.addEventListener('click', (event) => {this.onClick(event)}); + this.isInitialized = false; + this.userId = listContainerElement.dataset.userId; + app.subscribeUser(this.userId).then((response) => { + app.socket.on('PATCH', (patch) => { + if (this.isInitialized) {this.onPatch(patch);} + }); + }); + app.getUser(this.userId).then((user) => { + this.add(Object.values(user.corpora)); + this.isInitialized = true; + }); + } + + // #region Mandatory getters and methods to implement + get item() { + return ` + + book +
+ + + delete + send + + + `.trim(); + } + + get valueNames() { + return [ + {data: ['id']}, + {data: ['creation-date']}, + {name: 'status', attr: 'data-status'}, + 'description', + 'title' + ]; + } + + initListContainerElement() { + if (!this.listContainerElement.hasAttribute('id')) { + this.listContainerElement.id = Utils.generateElementId('corpus-list-'); + } + let listSearchElementId = Utils.generateElementId(`${this.listContainerElement.id}-search-`); + this.listContainerElement.innerHTML = ` +
+ search + + +
+ + + + + + + + + + +
Title and DescriptionStatus
+ + `.trim(); + } + // #endregion + + // #region Optional methods to implement + mapResourceToValue(corpus) { + return { + 'id': corpus.id, + 'creation-date': corpus.creation_date, + 'description': corpus.description, + 'status': corpus.status, + 'title': corpus.title + }; + } + + sort() { + this.listjs.sort('creation-date', {order: 'desc'}); + } + // #endregion + + onClick(event) { + let corpusElement = event.target.closest('tr'); + if (corpusElement === null) {return;} + let corpusId = corpusElement.dataset.id; + if (corpusId === undefined) {return;} + let actionButtonElement = event.target.closest('.action-button'); + let action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action; + switch (action) { + case 'delete-request': { + Utils.deleteCorpusRequest(this.userId, corpusId); + break; + } + case 'view': { + window.location.href = `/corpora/${corpusId}`; + break; + } + default: { + break; + } + } + } + + onPatch(patch) { + let re = new RegExp(`^/users/${this.userId}/corpora/([A-Za-z0-9]*)`); + let filteredPatch = patch.filter(operation => re.test(operation.path)); + for (let operation of filteredPatch) { + switch(operation.op) { + case 'add': { + let re = new RegExp(`^/users/${this.userId}/corpora/([A-Za-z0-9]*)$`); + if (re.test(operation.path)) {this.add(operation.value);} + break; + } + case 'remove': { + let re = new RegExp(`^/users/${this.userId}/corpora/([A-Za-z0-9]*)$`); + if (re.test(operation.path)) { + let [match, corpusId] = operation.path.match(re); + this.remove(corpusId); + } + break; + } + case 'replace': { + let re = new RegExp(`^/users/${this.userId}/corpora/([A-Za-z0-9]*)/(status|description|title)$`); + if (re.test(operation.path)) { + let [match, corpusId, valueName] = operation.path.match(re); + this.replace(corpusId, valueName, operation.value); + } + break; + } + default: { + break; + } + } + } + } +} diff --git a/app/static/js/ResourceLists/JobInputList.js b/app/static/js/ResourceLists/JobInputList.js new file mode 100644 index 00000000..02b20df4 --- /dev/null +++ b/app/static/js/ResourceLists/JobInputList.js @@ -0,0 +1,101 @@ +class JobInputList extends ResourceList { + static autoInit() { + for (let jobInputListElement of document.querySelectorAll('.job-input-list:not(.no-autoinit)')) { + new JobInputList(jobInputListElement); + } + } + + constructor(listContainerElement, options = {}) { + super(listContainerElement, options); + this.listjs.list.addEventListener('click', (event) => {this.onClick(event)}); + this.isInitialized = false; + this.userId = listContainerElement.dataset.userId; + this.jobId = listContainerElement.dataset.jobId; + app.subscribeUser(this.userId).then((response) => { + app.socket.on('PATCH', (patch) => { + if (this.isInitialized) {this.onPatch(patch);} + }); + }); + app.getUser(this.userId).then((user) => { + this.add(Object.values(user.jobs[this.jobId].inputs)); + this.isInitialized = true; + }); + } + + // #region Mandatory getters and methods to implement + get item() { + return ` + + + + file_download + + + `.trim(); + } + + get valueNames() { + return [ + {data: ['id']}, + {data: ['creation-date']}, + 'filename' + ]; + } + + initListContainerElement() { + if (!this.listContainerElement.hasAttribute('id')) { + this.listContainerElement.id = Utils.generateElementId('job-input-list-'); + } + let listSearchElementId = Utils.generateElementId(`${this.listContainerElement.id}-search-`); + this.listContainerElement.innerHTML = ` +
+ search + + +
+ + + + + + + + +
Filename
+ + `.trim(); + } + // #endregion + + // #region Optional methods to implement + mapResourceToValue(jobInput) { + return { + 'id': jobInput.id, + 'creation-date': jobInput.creation_date, + 'filename': jobInput.filename + }; + } + + sort() { + this.listjs.sort('filename', {order: 'asc'}); + } + // #endregion + + onClick(event) { + let jobInputElement = event.target.closest('tr'); + if (jobInputElement === null) {return;} + let jobInputId = jobInputElement.dataset.id; + if (jobInputId === undefined) {return;} + let actionButtonElement = event.target.closest('.action-button'); + let action = actionButtonElement === null ? 'download' : actionButtonElement.dataset.action; + switch (action) { + case 'download': { + window.location.href = `/jobs/${this.jobId}/inputs/${jobInputId}/download`; + break; + } + default: { + break; + } + } + } +} diff --git a/app/static/js/ResourceLists/JobList.js b/app/static/js/ResourceLists/JobList.js new file mode 100644 index 00000000..6800b520 --- /dev/null +++ b/app/static/js/ResourceLists/JobList.js @@ -0,0 +1,148 @@ +class JobList extends ResourceList { + static autoInit() { + for (let jobListElement of document.querySelectorAll('.job-list:not(.no-autoinit)')) { + new JobList(jobListElement); + } + } + + constructor(listContainerElement, options = {}) { + super(listContainerElement, options); + this.listjs.list.addEventListener('click', (event) => {this.onClick(event)}); + this.isInitialized = false; + this.userId = listContainerElement.dataset.userId; + app.subscribeUser(this.userId).then((response) => { + app.socket.on('PATCH', (patch) => { + if (this.isInitialized) {this.onPatch(patch);} + }); + }); + app.getUser(this.userId).then((user) => { + this.add(Object.values(user.jobs)); + this.isInitialized = true; + }); + } + + // #region Mandatory getters and methods to implement + get item() { + return ` + + +
+ + + delete + send + + + `.trim(); + } + + get valueNames() { + return [ + {data: ['id']}, + {data: ['creation-date']}, + {data: ['service']}, + {name: 'status', attr: 'data-status'}, + 'description', + 'title' + ]; + } + + initListContainerElement() { + if (!this.listContainerElement.hasAttribute('id')) { + this.listContainerElement.id = Utils.generateElementId('job-list-'); + } + let listSearchElementId = Utils.generateElementId(`${this.listContainerElement.id}-search-`); + this.listContainerElement.innerHTML = ` +
+ search + + +
+ + + + + + + + + + +
ServiceTitle and DescriptionStatus
+ + `.trim(); + } + // #endregion + + // #region Optional methods to implement + mapResourceToValue(job) { + return { + 'id': job.id, + 'creation-date': job.creation_date, + 'description': job.description, + 'service': job.service, + 'status': job.status, + 'title': job.title + }; + } + + sort() { + this.listjs.sort('creation-date', {order: 'desc'}); + } + // #endregion + + onClick(event) { + let jobElement = event.target.closest('tr'); + if (jobElement === null) {return;} + let jobId = jobElement.dataset.id; + if (jobId === undefined) {return;} + let actionButtonElement = event.target.closest('.action-button'); + let action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action; + switch (action) { + case 'delete-request': { + Utils.deleteJobRequest(this.userId, jobId); + break; + } + case 'view': { + window.location.href = `/jobs/${jobId}`; + break; + } + default: { + break; + } + } + } + + onPatch(patch) { + let re = new RegExp(`^/users/${this.userId}/jobs/([A-Za-z0-9]*)`); + let filteredPatch = patch.filter(operation => re.test(operation.path)); + for (let operation of filteredPatch) { + switch(operation.op) { + case 'add': { + let re = new RegExp(`^/users/${this.userId}/jobs/([A-Za-z0-9]*)$`); + if (re.test(operation.path)) {this.add(operation.value);} + break; + } + case 'remove': { + let re = new RegExp(`^/users/${this.userId}/jobs/([A-Za-z0-9]*)$`); + if (re.test(operation.path)) { + let [match, jobId] = operation.path.match(re); + this.remove(jobId); + } + break; + } + case 'replace': { + let re = new RegExp(`^/users/${this.userId}/jobs/([A-Za-z0-9]*)/(service|status|description|title)$`); + if (re.test(operation.path)) { + let [match, jobId, valueName] = operation.path.match(re); + this.replace(jobId, valueName, operation.value); + } + break; + } + default: { + break; + } + } + } + } +} diff --git a/app/static/js/ResourceLists/JobResultList.js b/app/static/js/ResourceLists/JobResultList.js new file mode 100644 index 00000000..06bd20ec --- /dev/null +++ b/app/static/js/ResourceLists/JobResultList.js @@ -0,0 +1,122 @@ +class JobResultList extends ResourceList { + static autoInit() { + for (let jobResultListElement of document.querySelectorAll('.job-result-list:not(.no-autoinit)')) { + new JobResultList(jobResultListElement); + } + } + + constructor(listContainerElement, options = {}) { + super(listContainerElement, options); + this.listjs.list.addEventListener('click', (event) => {this.onClick(event)}); + this.isInitialized = false; + this.userId = listContainerElement.dataset.userId; + this.jobId = listContainerElement.dataset.jobId; + app.subscribeUser(this.userId).then((response) => { + app.socket.on('PATCH', (patch) => { + if (this.isInitialized) {this.onPatch(patch);} + }); + }); + app.getUser(this.userId).then((user) => { + this.add(Object.values(user.jobs[this.jobId].results)); + this.isInitialized = true; + }); + } + + // #region Mandatory getters and methods to implement + get item() { + return ` + + + + + file_download + + + `.trim(); + } + + get valueNames() { + return [ + {data: ['id']}, + {data: ['creation-date']}, + 'description', + 'filename' + ]; + } + + initListContainerElement() { + if (!this.listContainerElement.hasAttribute('id')) { + this.listContainerElement.id = Utils.generateElementId('job-result-list-'); + } + let listSearchElementId = Utils.generateElementId(`${this.listContainerElement.id}-search-`); + this.listContainerElement.innerHTML = ` +
+ search + + +
+ + + + + + + + + +
DescriptionFilename
+ + `.trim(); + } + // #endregion + + // #region Optional methods to implement + mapResourceToValue(jobResult) { + return { + 'id': jobResult.id, + 'creation-date': jobResult.creation_date, + 'description': jobResult.description, + 'filename': jobResult.filename + }; + } + + sort() { + this.listjs.sort('filename', {order: 'asc'}); + } + // #endregion + + onClick(event) { + let jobResultElement = event.target.closest('tr'); + if (jobResultElement === null) {return;} + let jobResultId = jobResultElement.dataset.id; + if (jobResultId === undefined) {return;} + let actionButtonElement = event.target.closest('.action-button'); + let action = actionButtonElement === null ? 'download' : actionButtonElement.dataset.action; + switch (action) { + case 'download': { + window.location.href = `/jobs/${this.jobId}/results/${jobResultId}/download`; + break; + } + default: { + break; + } + } + } + + onPatch(patch) { + let re = new RegExp(`^/users/${this.userId}/jobs/${this.jobId}/results/([A-Za-z0-9]*)`); + let filteredPatch = patch.filter(operation => re.test(operation.path)); + for (let operation of filteredPatch) { + switch(operation.op) { + case 'add': { + let re = new RegExp(`^/users/${this.userId}/jobs/${this.jobId}/results/([A-Za-z0-9]*)$`); + if (re.test(operation.path)) {this.add(operation.value);} + break; + } + default: { + break; + } + } + } + } +} diff --git a/app/static/js/ResourceLists/PublicUserList.js b/app/static/js/ResourceLists/PublicUserList.js new file mode 100644 index 00000000..447ff3fe --- /dev/null +++ b/app/static/js/ResourceLists/PublicUserList.js @@ -0,0 +1,107 @@ +class PublicUserList extends ResourceList { + static autoInit() { + for (let publicUserListElement of document.querySelectorAll('.public-user-list:not(.no-autoinit)')) { + new PublicUserList(publicUserListElement); + } + } + + constructor(listContainerElement, options = {}) { + super(listContainerElement, options); + this.listjs.list.addEventListener('click', (event) => {this.onClick(event)}); + } + + // #region Mandatory getters and methods to implement + get item() { + return ` + + user-image + + + + + + + send + + + `.trim(); + } + + get valueNames() { + return [ + {data: ['id']}, + {data: ['member-since']}, + {name: 'avatar', attr: 'src'}, + 'username', + 'full-name', + 'location', + 'organization', + 'corpora-online' + ]; + } + + initListContainerElement() { + if (!this.listContainerElement.hasAttribute('id')) { + this.listContainerElement.id = Utils.generateElementId('public-user-list-'); + } + let listSearchElementId = Utils.generateElementId(`${this.listContainerElement.id}-search-`); + this.listContainerElement.innerHTML = ` +
+ search + + +
+ + + + + + + + + + + + + +
UsernameFull nameLocationOrganizationCorpora online
+ + `.trim(); + } + // #endregion + + // #region Optional methods to implement + mapResourceToValue(user) { + return { + 'id': user.id, + 'member-since': user.member_since, + 'avatar': user.avatar ? `/users/${user.id}/avatar` : '/static/images/user_avatar.png', + 'username': user.username, + 'full-name': user.full_name ? user.full_name : '', + 'location': user.location ? user.location : '', + 'organization': user.organization ? user.organization : '', + 'corpora-online': '0' + }; + }; + + sort() { + this.listjs.sort('member-since', {order: 'desc'}); + } + // #endregion + + onClick(event) { + let actionButtonElement = event.target.closest('.action-button'); + let action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action; + let publicUserElement = event.target.closest('tr'); + let publicUserId = publicUserElement.dataset.id; + switch (action) { + case 'view': { + window.location.href = `/users/${publicUserId}`; + break; + } + default: { + break; + } + } + } +} diff --git a/app/static/js/ResourceLists/ResourceList.js b/app/static/js/ResourceLists/ResourceList.js new file mode 100644 index 00000000..f2d917d2 --- /dev/null +++ b/app/static/js/ResourceLists/ResourceList.js @@ -0,0 +1,70 @@ +class ResourceList { + /* A wrapper class for the list.js list. + * This class is not meant to be used directly, instead it should be used as + * a base class for concrete resource list implementations. + */ + + static autoInit() { + CorpusList.autoInit(); + CorpusFileList.autoInit(); + JobList.autoInit(); + JobInputList.autoInit(); + JobResultList.autoInit(); + PublicUserList.autoInit(); + SpaCyNLPPipelineModelList.autoInit(); + TesseractOCRPipelineModelList.autoInit(); + UserList.autoInit(); + } + + static defaultOptions = { + page: 5, + pagination: { + innerWindow: 2, + outerWindow: 2 + } + }; + + constructor(listContainerElement, options={}) { + if ('items' in options) {throw '"items" is not supported as an option, define it as a getter in the list class';} + if ('valueNames' in options) {throw '"valueNames" is not supported as an option, define it as a getter in the list class';} + let _options = _.merge({item: this.item, valueNames: this.valueNames}, ResourceList.defaultOptions, options); + this.listContainerElement = listContainerElement; + this.initListContainerElement(); + this.listjs = new List(listContainerElement, _options); + } + + add(resources, callback) { + let values = resources.map((resource) => { + return this.mapResourceToValue(resource); + }); + this.listjs.add(values, (items) => { + this.sort(); + if (typeof callback === 'function') { + callback(items); + } + }); + } + + remove(id) { + this.listjs.remove('id', id); + } + + replace(id, key, value) { + let item = this.listjs.get('id', id)[0]; + item.values({[key]: value}); + } + + // #region Mandatory getters and methods to implement + get item() {throw 'Not implemented';} + + get valueNames() {throw 'Not implemented';} + + initListContainerElement() {throw 'Not implemented';} + // #endregion + + // #region Optional methods to implement + mapResourceToValue(resource) {return resource;} + + sort() {return;} + // #endregion +} diff --git a/app/static/js/ResourceLists/SpacyNLPPipelineModelList.js b/app/static/js/ResourceLists/SpacyNLPPipelineModelList.js new file mode 100644 index 00000000..8f820959 --- /dev/null +++ b/app/static/js/ResourceLists/SpacyNLPPipelineModelList.js @@ -0,0 +1,166 @@ +class SpaCyNLPPipelineModelList extends ResourceList { + static autoInit() { + for (let spaCyNLPPipelineModelListElement of document.querySelectorAll('.spacy-nlp-pipeline-model-list:not(.no-autoinit)')) { + new SpaCyNLPPipelineModelList(spaCyNLPPipelineModelListElement); + } + } + + constructor(listContainerElement, options = {}) { + super(listContainerElement, options); + this.listjs.list.addEventListener('change', (event) => {this.onChange(event)}); + this.listjs.list.addEventListener('click', (event) => {this.onClick(event)}); + this.isInitialized = false; + this.userId = listContainerElement.dataset.userId; + app.subscribeUser(this.userId).then((response) => { + app.socket.on('PATCH', (patch) => { + if (this.isInitialized) {this.onPatch(patch);} + }); + }); + app.getUser(this.userId).then((user) => { + this.add(Object.values(user.spacy_nlp_pipeline_models)); + for (let uncheckedCheckbox of this.listjs.list.querySelectorAll('input[data-checked="True"]')) { + uncheckedCheckbox.setAttribute('checked', ''); + } + if (user.role.name !== ('Administrator' || 'Contributor')) { + for (let switchElement of this.listjs.list.querySelectorAll('.is_public')) { + switchElement.setAttribute('disabled', ''); + } + } + this.isInitialized = true; + }); + } + + // #region Mandatory getters and methods to implement + get item() { + return ` + +
+ ()
+ +
+ + +
+ + + delete + send + + + `.trim(); + } + + get valueNames() { + return [ + {data: ['id']}, + {data: ['creation-date']}, + {name: 'publisher-url', attr: 'href'}, + {name: 'publishing-url', attr: 'href'}, + 'description', + 'publisher', + 'publishing-url-2', + 'publishing-year', + 'title', + 'title-2', + 'version', + {name: 'is_public', attr: 'data-checked'} + ]; + } + + initListContainerElement() { + if (!this.listContainerElement.hasAttribute('id')) { + this.listContainerElement.id = Utils.generateElementId('spacy-nlp-pipeline-model-list-'); + } + let listSearchElementId = Utils.generateElementId(`${this.listContainerElement.id}-search-`); + this.listContainerElement.innerHTML = ` +
+ search + + +
+ + + + + + + + + +
Title and DescriptionPublisher
+ + `.trim(); + } + // #endregion + + // #region Optional methods to implement + mapResourceToValue(spaCyNLPPipelineModel) { + return { + 'id': spaCyNLPPipelineModel.id, + 'creation-date': spaCyNLPPipelineModel.creation_date, + 'description': spaCyNLPPipelineModel.description, + 'publisher': spaCyNLPPipelineModel.publisher, + 'publisher-url': spaCyNLPPipelineModel.publisher_url, + 'publishing-url': spaCyNLPPipelineModel.publishing_url, + 'publishing-url-2': spaCyNLPPipelineModel.publishing_url, + 'publishing-year': spaCyNLPPipelineModel.publishing_year, + 'title': spaCyNLPPipelineModel.title, + 'title-2': spaCyNLPPipelineModel.title, + 'version': spaCyNLPPipelineModel.version, + 'is_public': spaCyNLPPipelineModel.is_public ? 'True' : 'False' + }; + } + + sort() { + this.listjs.sort('creation-date', {order: 'desc'}); + } + // #endregion + + onChange(event) { + let actionSwitchElement = event.target.closest('.action-switch'); + let action = actionSwitchElement.dataset.action; + let spaCyNLPPipelineModelElement = event.target.closest('tr'); + let spaCyNLPPipelineModelId = spaCyNLPPipelineModelElement.dataset.id; + switch (action) { + case 'share-request': { + let is_public = actionSwitchElement.querySelector('input').checked; + Utils.shareSpaCyNLPPipelineModelRequest(this.userId, spaCyNLPPipelineModelId, is_public); + break; + } + default: { + break; + } + } + } + + onClick(event) { + if (event.target.closest('.action-switch')) { + let userRole = app.data.users[this.userId].role.name; + if (userRole !== ('Administrator' || 'Contributor')) { + app.flash('You need the "Contributor" or "Administrator" role to perform this action.', 'error'); + } + return; + } + let actionButtonElement = event.target.closest('.action-button'); + let action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action; + let spaCyNLPPipelineModelElement = event.target.closest('tr'); + let spaCyNLPPipelineModelId = spaCyNLPPipelineModelElement.dataset.id; + switch (action) { + case 'delete-request': { + Utils.deleteSpaCyNLPPipelineModelRequest(this.userId, spaCyNLPPipelineModelId); + break; + } + case 'view': { + window.location.href = `/contributions/spacy-nlp-pipeline-models/${spaCyNLPPipelineModelId}`; + break; + } + default: { + break; + } + } + } +} diff --git a/app/static/js/ResourceLists/TesseractOCRPipelineModelList.js b/app/static/js/ResourceLists/TesseractOCRPipelineModelList.js new file mode 100644 index 00000000..98506537 --- /dev/null +++ b/app/static/js/ResourceLists/TesseractOCRPipelineModelList.js @@ -0,0 +1,166 @@ +class TesseractOCRPipelineModelList extends ResourceList { + static autoInit() { + for (let tesseractOCRPipelineModelListElement of document.querySelectorAll('.tesseract-ocr-pipeline-model-list:not(.no-autoinit)')) { + new TesseractOCRPipelineModelList(tesseractOCRPipelineModelListElement); + } + } + + constructor(listContainerElement, options = {}) { + super(listContainerElement, options); + this.listjs.list.addEventListener('change', (event) => {this.onChange(event)}); + this.listjs.list.addEventListener('click', (event) => {this.onClick(event)}); + this.isInitialized = false; + this.userId = listContainerElement.dataset.userId; + app.subscribeUser(this.userId).then((response) => { + app.socket.on('PATCH', (patch) => { + if (this.isInitialized) {this.onPatch(patch);} + }); + }); + app.getUser(this.userId).then((user) => { + this.add(Object.values(user.tesseract_ocr_pipeline_models)); + for (let uncheckedCheckbox of this.listjs.list.querySelectorAll('input[data-checked="True"]')) { + uncheckedCheckbox.setAttribute('checked', ''); + } + if (user.role.name !== ('Administrator' || 'Contributor')) { + for (let switchElement of this.listjs.list.querySelectorAll('.is_public')) { + switchElement.setAttribute('disabled', ''); + } + } + this.isInitialized = true; + }); + } + + // #region Mandatory getters and methods to implement + get item() { + return ` + +
+ ()
+ +
+ + +
+ + + delete + send + + + `.trim(); + } + + get valueNames() { + return [ + {data: ['id']}, + {data: ['creation-date']}, + {name: 'publisher-url', attr: 'href'}, + {name: 'publishing-url', attr: 'href'}, + 'description', + 'publisher', + 'publishing-url-2', + 'publishing-year', + 'title', + 'title-2', + 'version', + {name: 'is_public', attr: 'data-checked'} + ]; + } + + initListContainerElement() { + if (!this.listContainerElement.hasAttribute('id')) { + this.listContainerElement.id = Utils.generateElementId('tesseract-ocr-pipeline-model-list-'); + } + let listSearchElementId = Utils.generateElementId(`${this.listContainerElement.id}-search-`); + this.listContainerElement.innerHTML = ` +
+ search + + +
+ + + + + + + + + +
Title and DescriptionPublisher
+ + `.trim(); + } + // #endregion + + // #region Optional methods to implement + mapResourceToValue(tesseractOCRPipelineModel) { + return { + 'id': tesseractOCRPipelineModel.id, + 'creation-date': tesseractOCRPipelineModel.creation_date, + 'description': tesseractOCRPipelineModel.description, + 'publisher': tesseractOCRPipelineModel.publisher, + 'publisher-url': tesseractOCRPipelineModel.publisher_url, + 'publishing-url': tesseractOCRPipelineModel.publishing_url, + 'publishing-url-2': tesseractOCRPipelineModel.publishing_url, + 'publishing-year': tesseractOCRPipelineModel.publishing_year, + 'title': tesseractOCRPipelineModel.title, + 'title-2': tesseractOCRPipelineModel.title, + 'version': tesseractOCRPipelineModel.version, + 'is_public': tesseractOCRPipelineModel.is_public ? 'True' : 'False' + }; + } + + sort() { + this.listjs.sort('creation-date', {order: 'desc'}); + } + // #endregion + + onChange(event) { + let actionSwitchElement = event.target.closest('.action-switch'); + let action = actionSwitchElement.dataset.action; + let tesseractOCRPipelineModelElement = event.target.closest('tr'); + let tesseractOCRPipelineModelId = tesseractOCRPipelineModelElement.dataset.id; + switch (action) { + case 'share-request': { + let is_public = actionSwitchElement.querySelector('input').checked; + Utils.shareTesseractOCRPipelineModelRequest(this.userId, tesseractOCRPipelineModelId, is_public); + break; + } + default: { + break; + } + } + } + + onClick(event) { + if (event.target.closest('.action-switch')) { + let userRole = app.data.users[this.userId].role.name; + if (userRole !== ('Administrator' || 'Contributor')) { + app.flash('You need the "Contributor" or "Administrator" role to perform this action.', 'error'); + } + return; + } + let actionButtonElement = event.target.closest('.action-button'); + let action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action; + let tesseractOCRPipelineModelElement = event.target.closest('tr'); + let tesseractOCRPipelineModelId = tesseractOCRPipelineModelElement.dataset.id; + switch (action) { + case 'delete-request': { + Utils.deleteTesseractOCRPipelineModelRequest(this.userId, tesseractOCRPipelineModelId); + break; + } + case 'view': { + window.location.href = `/contributions/tesseract-ocr-pipeline-models/${tesseractOCRPipelineModelId}`; + break; + } + default: { + break; + } + } + } +} diff --git a/app/static/js/ResourceLists/UserList.js b/app/static/js/ResourceLists/UserList.js new file mode 100644 index 00000000..e2ee891b --- /dev/null +++ b/app/static/js/ResourceLists/UserList.js @@ -0,0 +1,116 @@ +class UserList extends ResourceList { + static autoInit() { + for (let userListElement of document.querySelectorAll('.user-list:not(.no-autoinit)')) { + new UserList(userListElement); + } + } + + constructor(listContainerElement, options = {}) { + super(listContainerElement, options); + this.listjs.list.addEventListener('click', (event) => {this.onClick(event)}); + } + + // #region Mandatory getters and methods to implement + get item() { + return ` + + + + + + + + delete + edit + send + + + `.trim(); + } + + get valueNames() { + return [ + {data: ['id']}, + {data: ['member-since']}, + 'email', + 'id-1', + 'last-seen', + 'role', + 'username' + ]; + } + + initListContainerElement() { + if (!this.listContainerElement.hasAttribute('id')) { + this.listContainerElement.id = Utils.generateElementId('user-list-'); + } + let listSearchElementId = Utils.generateElementId(`${this.listContainerElement.id}-search-`); + this.listContainerElement.innerHTML = ` +
+ search + + +
+ + + + + + + + + + + + +
IdUsernameEmailLast seenRole
+ + `.trim(); + } + // #endregion + + // #region Optional methods to implement + mapResourceToValue(user) { + return { + 'id': user.id, + 'id-1': user.id, + 'username': user.username, + 'email': user.email, + 'last-seen': new Date(user.last_seen).toLocaleString('en-US'), + 'member-since': user.member_since, + 'role': user.role.name + }; + } + + sort() { + this.listjs.sort('member-since', {order: 'desc'}); + } + // #endregion + + onClick(event) { + let userElement = event.target.closest('tr'); + if (userElement === null) {return;} + let userId = userElement.dataset.id; + if (userId === undefined) {return;} + let actionButtonElement = event.target.closest('.action-button'); + let action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action; + switch (action) { + case 'delete': { + Utils.deleteUserRequest(userId); + if (userId === currentUserId) {window.location.href = '/';} + break; + } + case 'edit': { + window.location.href = `/admin/users/${userId}/edit`; + break; + } + case 'view': { + window.location.href = `/admin/users/${userId}`; + break; + } + default: { + break; + } + } + } +} diff --git a/app/static/js/RessourceLists/CorpusFileList.js b/app/static/js/RessourceLists/CorpusFileList.js deleted file mode 100644 index af68c9dc..00000000 --- a/app/static/js/RessourceLists/CorpusFileList.js +++ /dev/null @@ -1,134 +0,0 @@ -class CorpusFileList extends RessourceList { - static autoInit() { - for (let corpusFileListElement of document.querySelectorAll('.corpus-file-list:not(.no-autoinit)')) { - new CorpusFileList(corpusFileListElement); - } - } - - static options = { - listContainerInnerHTMLGenerator: (listContainerElement) => { - listContainerElement.innerHTML = ` -
- search - - -
- - - - - - - - - - - -
FilenameAuthorTitlePublishing year
- - `.trim(); - }, - ressourceMapper: (corpusFile) => { - return { - 'id': corpusFile.id, - 'author': corpusFile.author, - 'creation-date': corpusFile.creation_date, - 'filename': corpusFile.filename, - 'publishing-year': corpusFile.publishing_year, - 'title': corpusFile.title - }; - }, - sortParams: ['creation-date', {order: 'desc'}], - listjs: { - item: ` - - - - - - - delete - file_download - send - - - `.trim(), - valueNames: [ - {data: ['id']}, - {data: ['creation-date']}, - 'author', - 'filename', - 'publishing-year', - 'title' - ] - } - }; - - constructor(listContainerElement, options={}) { - super(listContainerElement, _.merge({}, CorpusFileList.options, options)); - this.corpusId = listContainerElement.dataset.corpusId; - } - - init(user) { - this._init(user.corpora[this.corpusId].files); - } - - onClick(event) { - let corpusFileElement = event.target.closest('tr'); - if (corpusFileElement === null) {return;} - let corpusFileId = corpusFileElement.dataset.id; - if (corpusFileId === undefined) {return;} - let actionButtonElement = event.target.closest('.action-button'); - let action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action; - switch (action) { - case 'delete': { - Utils.deleteCorpusFileRequest(this.userId, this.corpusId, corpusFileId); - break; - } - case 'download': { - window.location.href = `/corpora/${this.corpusId}/files/${corpusFileId}/download`; - break; - } - case 'view': { - window.location.href = `/corpora/${this.corpusId}/files/${corpusFileId}`; - break; - } - default: { - break; - } - } - } - - onPatch(patch) { - let re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/files/([A-Za-z0-9]*)`); - let filteredPatch = patch.filter(operation => re.test(operation.path)); - for (let operation of filteredPatch) { - switch(operation.op) { - case 'add': { - let re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/files/([A-Za-z0-9]*)$`); - if (re.test(operation.path)) {this.add(operation.value);} - break; - } - case 'remove': { - let re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/files/([A-Za-z0-9]*)$`); - if (re.test(operation.path)) { - let [match, corpusFileId] = operation.path.match(re); - this.remove(corpusFileId); - } - break; - } - case 'replace': { - let re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/files/([A-Za-z0-9]*)/(author|filename|publishing_year|title)$`); - if (re.test(operation.path)) { - let [match, corpusFileId, valueName] = operation.path.match(re); - this.replace(corpusFileId, valueName.replace('_', '-'), operation.value); - } - break; - } - default: { - break; - } - } - } - } -} diff --git a/app/static/js/RessourceLists/CorpusList.js b/app/static/js/RessourceLists/CorpusList.js deleted file mode 100644 index fa0fa704..00000000 --- a/app/static/js/RessourceLists/CorpusList.js +++ /dev/null @@ -1,124 +0,0 @@ -class CorpusList extends RessourceList { - static autoInit() { - for (let corpusListElement of document.querySelectorAll('.corpus-list:not(.no-autoinit)')) { - new CorpusList(corpusListElement); - } - } - - static options = { - listContainerInnerHTMLGenerator: (listContainerElement) => { - listContainerElement.innerHTML = ` -
- search - - -
- - - - - - - - - - -
Title and DescriptionStatus
- - `.trim(); - }, - ressourceMapper: (corpus) => { - return { - 'id': corpus.id, - 'creation-date': corpus.creation_date, - 'description': corpus.description, - 'status': corpus.status, - 'title': corpus.title - }; - }, - sortParams: ['creation-date', {order: 'desc'}], - listjs: { - item: ` - - book -
- - - delete - send - - - `.trim(), - valueNames: [ - {data: ['id']}, - {data: ['creation-date']}, - {name: 'status', attr: 'data-status'}, - 'description', - 'title' - ] - } - }; - - constructor(listContainerElement, options={}) { - super(listContainerElement, _.merge({}, CorpusList.options, options)); - } - - init(user) { - this._init(user.corpora); - } - - onClick(event) { - let corpusElement = event.target.closest('tr'); - if (corpusElement === null) {return;} - let corpusId = corpusElement.dataset.id; - if (corpusId === undefined) {return;} - let actionButtonElement = event.target.closest('.action-button'); - let action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action; - switch (action) { - case 'delete-request': { - Utils.deleteCorpusRequest(this.userId, corpusId); - break; - } - case 'view': { - window.location.href = `/corpora/${corpusId}`; - break; - } - default: { - break; - } - } - } - - onPatch(patch) { - let re = new RegExp(`^/users/${this.userId}/corpora/([A-Za-z0-9]*)`); - let filteredPatch = patch.filter(operation => re.test(operation.path)); - for (let operation of filteredPatch) { - switch(operation.op) { - case 'add': { - let re = new RegExp(`^/users/${this.userId}/corpora/([A-Za-z0-9]*)$`); - if (re.test(operation.path)) {this.add(operation.value);} - break; - } - case 'remove': { - let re = new RegExp(`^/users/${this.userId}/corpora/([A-Za-z0-9]*)$`); - if (re.test(operation.path)) { - let [match, corpusId] = operation.path.match(re); - this.remove(corpusId); - } - break; - } - case 'replace': { - let re = new RegExp(`^/users/${this.userId}/corpora/([A-Za-z0-9]*)/(status|description|title)$`); - if (re.test(operation.path)) { - let [match, corpusId, valueName] = operation.path.match(re); - this.replace(corpusId, valueName, operation.value); - } - break; - } - default: { - break; - } - } - } - } -} diff --git a/app/static/js/RessourceLists/JobInputList.js b/app/static/js/RessourceLists/JobInputList.js deleted file mode 100644 index 069d470e..00000000 --- a/app/static/js/RessourceLists/JobInputList.js +++ /dev/null @@ -1,81 +0,0 @@ -class JobInputList extends RessourceList { - static autoInit() { - for (let jobInputListElement of document.querySelectorAll('.job-input-list:not(.no-autoinit)')) { - new JobInputList(jobInputListElement); - } - } - - static options = { - listContainerInnerHTMLGenerator: (listContainerElement) => { - listContainerElement.innerHTML = ` -
- search - - -
- - - - - - - - -
Filename
- - `.trim(); - }, - ressourceMapper: (jobInput) => { - return { - 'id': jobInput.id, - 'creation-date': jobInput.creation_date, - 'filename': jobInput.filename - }; - }, - sortParams: ['filename', {order: 'asc'}], - listjs: { - item: ` - - - - file_download - - - `.trim(), - valueNames: [ - {data: ['id']}, - {data: ['creation-date']}, - 'filename' - ] - } - }; - - constructor(listContainerElement, options={}) { - super(listContainerElement, _.merge({}, JobInputList.options, options)); - this.jobId = listContainerElement.dataset.jobId; - } - - init(user) { - this._init(user.jobs[this.jobId].inputs); - } - - onClick(event) { - let jobInputElement = event.target.closest('tr'); - if (jobInputElement === null) {return;} - let jobInputId = jobInputElement.dataset.id; - if (jobInputId === undefined) {return;} - let actionButtonElement = event.target.closest('.action-button'); - let action = actionButtonElement === null ? 'download' : actionButtonElement.dataset.action; - switch (action) { - case 'download': { - window.location.href = `/jobs/${this.jobId}/inputs/${jobInputId}/download`; - break; - } - default: { - break; - } - } - } - - onPatch(patch) {return;} -} diff --git a/app/static/js/RessourceLists/JobList.js b/app/static/js/RessourceLists/JobList.js deleted file mode 100644 index 3a8c6d8b..00000000 --- a/app/static/js/RessourceLists/JobList.js +++ /dev/null @@ -1,127 +0,0 @@ -class JobList extends RessourceList { - static autoInit() { - for (let jobListElement of document.querySelectorAll('.job-list:not(.no-autoinit)')) { - new JobList(jobListElement); - } - } - - static options = { - listContainerInnerHTMLGenerator: (listContainerElement) => { - listContainerElement.innerHTML = ` -
- search - - -
- - - - - - - - - - -
ServiceTitle and DescriptionStatus
- - `.trim(); - }, - ressourceMapper: (job) => { - return { - 'id': job.id, - 'creation-date': job.creation_date, - 'description': job.description, - 'service': job.service, - 'status': job.status, - 'title': job.title - }; - }, - sortParams: ['creation-date', {order: 'desc'}], - listjs: { - item: ` - - -
- - - delete - send - - - `.trim(), - valueNames: [ - {data: ['id']}, - {data: ['creation-date']}, - {data: ['service']}, - {name: 'status', attr: 'data-status'}, - 'description', - 'title' - ] - } - }; - - constructor(listContainerElement, options={}) { - super(listContainerElement, _.merge({}, JobList.options, options)); - console.log(this); - } - - init(user) { - this._init(user.jobs); - } - - onClick(event) { - let jobElement = event.target.closest('tr'); - if (jobElement === null) {return;} - let jobId = jobElement.dataset.id; - if (jobId === undefined) {return;} - let actionButtonElement = event.target.closest('.action-button'); - let action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action; - switch (action) { - case 'delete-request': { - Utils.deleteJobRequest(this.userId, jobId); - break; - } - case 'view': { - window.location.href = `/jobs/${jobId}`; - break; - } - default: { - break; - } - } - } - - onPatch(patch) { - let re = new RegExp(`^/users/${this.userId}/jobs/([A-Za-z0-9]*)`); - let filteredPatch = patch.filter(operation => re.test(operation.path)); - for (let operation of filteredPatch) { - switch(operation.op) { - case 'add': { - let re = new RegExp(`^/users/${this.userId}/jobs/([A-Za-z0-9]*)$`); - if (re.test(operation.path)) {this.add(operation.value);} - break; - } - case 'remove': { - let re = new RegExp(`^/users/${this.userId}/jobs/([A-Za-z0-9]*)$`); - if (re.test(operation.path)) { - let [match, jobId] = operation.path.match(re); - this.remove(jobId); - } - break; - } - case 'replace': { - let re = new RegExp(`^/users/${this.userId}/jobs/([A-Za-z0-9]*)/(service|status|description|title)$`); - if (re.test(operation.path)) { - let [match, jobId, valueName] = operation.path.match(re); - this.replace(jobId, valueName, operation.value); - } - break; - } - default: { - break; - } - } - } - } -} diff --git a/app/static/js/RessourceLists/JobResultList.js b/app/static/js/RessourceLists/JobResultList.js deleted file mode 100644 index ea7cfc3a..00000000 --- a/app/static/js/RessourceLists/JobResultList.js +++ /dev/null @@ -1,100 +0,0 @@ -class JobResultList extends RessourceList { - static autoInit() { - for (let jobResultListElement of document.querySelectorAll('.job-result-list:not(.no-autoinit)')) { - new JobResultList(jobResultListElement); - } - } - - static options = { - listContainerInnerHTMLGenerator: (listContainerElement) => { - listContainerElement.innerHTML = ` -
- search - - -
- - - - - - - - - -
DescriptionFilename
- - `.trim(); - }, - ressourceMapper: (jobResult) => { - return { - 'id': jobResult.id, - 'creation-date': jobResult.creation_date, - 'description': jobResult.description, - 'filename': jobResult.filename - }; - }, - sortParams: ['filename', {order: 'asc'}], - listjs: { - item: ` - - - - - file_download - - - `.trim(), - valueNames: [ - {data: ['id']}, - {data: ['creation-date']}, - 'description', - 'filename' - ] - } - }; - - constructor(listContainerElement, options = {}) { - super(listContainerElement, {...JobResultList.options, ...options}); - this.jobId = listContainerElement.dataset.jobId; - } - - init(user) { - super._init(user.jobs[this.jobId].results); - } - - onClick(event) { - let jobResultElement = event.target.closest('tr'); - if (jobResultElement === null) {return;} - let jobResultId = jobResultElement.dataset.id; - if (jobResultId === undefined) {return;} - let actionButtonElement = event.target.closest('.action-button'); - let action = actionButtonElement === null ? 'download' : actionButtonElement.dataset.action; - switch (action) { - case 'download': { - window.location.href = `/jobs/${this.jobId}/results/${jobResultId}/download`; - break; - } - default: { - break; - } - } - } - - onPatch(patch) { - let re = new RegExp(`^/users/${this.userId}/jobs/${this.jobId}/results/([A-Za-z0-9]*)`); - let filteredPatch = patch.filter(operation => re.test(operation.path)); - for (let operation of filteredPatch) { - switch(operation.op) { - case 'add': { - let re = new RegExp(`^/users/${this.userId}/jobs/${this.jobId}/results/([A-Za-z0-9]*)$`); - if (re.test(operation.path)) {this.add(operation.value);} - break; - } - default: { - break; - } - } - } - } -} diff --git a/app/static/js/RessourceLists/PublicUserList.js b/app/static/js/RessourceLists/PublicUserList.js deleted file mode 100644 index 16d95a32..00000000 --- a/app/static/js/RessourceLists/PublicUserList.js +++ /dev/null @@ -1,96 +0,0 @@ -class PublicUserList extends RessourceList { - static autoInit() { - for (let publicUserListElement of document.querySelectorAll('.public-user-list:not(.no-autoinit)')) { - new PublicUserList(publicUserListElement); - } - } - - static options = { - listContainerInnerHTMLGenerator: (listContainerElement) => { - listContainerElement.innerHTML = ` -
- search - - -
- - - - - - - - - - - - - -
UsernameFull nameLocationOrganizationCorpora online
- - `.trim(); - }, - ressourceMapper: (user) => { - return { - 'id': user.id, - 'member-since': user.member_since, - 'avatar': user.avatar ? `/users/${user.id}/avatar` : '/static/images/user_avatar.png', - 'username': user.username, - 'full-name': user.full_name ? user.full_name : '', - 'location': user.location ? user.location : '', - 'organization': user.organization ? user.organization : '', - 'corpora-online': '0' - }; - }, - sortParams: ['member-since', {order: 'desc'}], - listjs: { - item: ` - - user-image - - - - - - - send - - - `.trim(), - valueNames: [ - {data: ['id']}, - {data: ['member-since']}, - {name: 'avatar', attr: 'src'}, - 'username', - 'full-name', - 'location', - 'organization', - 'corpora-online' - ] - } - }; - - constructor(listContainerElement, options = {}) { - super(listContainerElement, {...PublicUserList.options, ...options}); - } - - init(users) { - super._init(Object.values(users)); - } - - onClick(event) { - let actionButtonElement = event.target.closest('.action-button'); - let action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action; - let publicUserElement = event.target.closest('tr'); - let publicUserId = publicUserElement.dataset.id; - switch (action) { - case 'view': { - window.location.href = `/users/${publicUserId}`; - break; - } - default: { - break; - } - } - } -} diff --git a/app/static/js/RessourceLists/SpacyNLPPipelineModelList.js b/app/static/js/RessourceLists/SpacyNLPPipelineModelList.js deleted file mode 100644 index e62428cf..00000000 --- a/app/static/js/RessourceLists/SpacyNLPPipelineModelList.js +++ /dev/null @@ -1,148 +0,0 @@ -class SpaCyNLPPipelineModelList extends RessourceList { - static autoInit() { - for (let spaCyNLPPipelineModelListElement of document.querySelectorAll('.spacy-nlp-pipeline-model-list:not(.no-autoinit)')) { - new SpaCyNLPPipelineModelList(spaCyNLPPipelineModelListElement); - } - } - - static options = { - listContainerInnerHTMLGenerator: (listContainerElement) => { - listContainerElement.innerHTML = ` -
- search - - -
- - - - - - - - - -
Title and DescriptionPublisher
-
    - `.trim(); - }, - ressourceMapper: (spaCyNLPPipelineModel) => { - return { - 'id': spaCyNLPPipelineModel.id, - 'creation-date': spaCyNLPPipelineModel.creation_date, - 'description': spaCyNLPPipelineModel.description, - 'publisher': spaCyNLPPipelineModel.publisher, - 'publisher-url': spaCyNLPPipelineModel.publisher_url, - 'publishing-url': spaCyNLPPipelineModel.publishing_url, - 'publishing-url-2': spaCyNLPPipelineModel.publishing_url, - 'publishing-year': spaCyNLPPipelineModel.publishing_year, - 'title': spaCyNLPPipelineModel.title, - 'title-2': spaCyNLPPipelineModel.title, - 'version': spaCyNLPPipelineModel.version, - 'is_public': spaCyNLPPipelineModel.is_public ? 'True' : 'False' - }; - }, - sortParams: ['creation-date', {order: 'desc'}], - listjs: { - item: ` - -
    - ()
    - -
    - - -
    - - - delete - send - - - `.trim(), - valueNames: [ - {data: ['id']}, - {data: ['creation-date']}, - {name: 'publisher-url', attr: 'href'}, - {name: 'publishing-url', attr: 'href'}, - 'description', - 'publisher', - 'publishing-url-2', - 'publishing-year', - 'title', - 'title-2', - 'version', - {name: 'is_public', attr: 'data-checked'} - ] - } - }; - - constructor(listContainerElement, options = {}) { - super(listContainerElement, {...SpaCyNLPPipelineModelList.options, ...options}); - this.listjs.list.addEventListener('change', (event) => {this.onChange(event)}); - } - - init(user) { - this._init(user.spacy_nlp_pipeline_models); - if (user.role.name !== ('Administrator' || 'Contributor')) { - for (let switchElement of this.listjs.list.querySelectorAll('.is_public')) { - switchElement.setAttribute('disabled', ''); - } - } - } - - _init(ressources) { - super._init(ressources); - for (let uncheckedCheckbox of this.listjs.list.querySelectorAll('input[data-checked="True"]')) { - uncheckedCheckbox.setAttribute('checked', ''); - } - } - - onClick(event) { - if (event.target.closest('.action-switch')) { - let userRole = app.data.users[this.userId].role.name; - if (userRole !== ('Administrator' || 'Contributor')) { - app.flash('You need the "Contributor" or "Administrator" role to perform this action.', 'error'); - } - return; - } - let actionButtonElement = event.target.closest('.action-button'); - let action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action; - let spaCyNLPPipelineModelElement = event.target.closest('tr'); - let spaCyNLPPipelineModelId = spaCyNLPPipelineModelElement.dataset.id; - switch (action) { - case 'delete-request': { - Utils.deleteSpaCyNLPPipelineModelRequest(this.userId, spaCyNLPPipelineModelId); - break; - } - case 'view': { - window.location.href = `/contributions/spacy-nlp-pipeline-models/${spaCyNLPPipelineModelId}`; - break; - } - default: { - break; - } - } - } - - onChange(event) { - let actionSwitchElement = event.target.closest('.action-switch'); - let action = actionSwitchElement.dataset.action; - let spaCyNLPPipelineModelElement = event.target.closest('tr'); - let spaCyNLPPipelineModelId = spaCyNLPPipelineModelElement.dataset.id; - switch (action) { - case 'share-request': { - let is_public = actionSwitchElement.querySelector('input').checked; - Utils.shareSpaCyNLPPipelineModelRequest(this.userId, spaCyNLPPipelineModelId, is_public); - break; - } - default: { - break; - } - } - } -} diff --git a/app/static/js/RessourceLists/UserList.js b/app/static/js/RessourceLists/UserList.js deleted file mode 100644 index 09a25f21..00000000 --- a/app/static/js/RessourceLists/UserList.js +++ /dev/null @@ -1,105 +0,0 @@ -class UserList extends RessourceList { - static autoInit() { - for (let userListElement of document.querySelectorAll('.user-list:not(.no-autoinit)')) { - new UserList(userListElement); - } - } - - static options = { - listContainerInnerHTMLGenerator: (listContainerElement) => { - listContainerElement.innerHTML = ` -
    - search - - -
    - - - - - - - - - - - - -
    IdUsernameEmailLast seenRole
    -
      - `.trim(); - }, - ressourceMapper: (user) => { - return { - 'id': user.id, - 'id-1': user.id, - 'username': user.username, - 'email': user.email, - 'last-seen': new Date(user.last_seen).toLocaleString('en-US'), - 'member-since': user.member_since, - 'role': user.role.name - }; - }, - sortParams: ['member-since', {order: 'desc'}], - listjs: { - item: ` - - - - - - - - delete - edit - send - - - `.trim(), - valueNames: [ - {data: ['id']}, - {data: ['member-since']}, - 'email', - 'id-1', - 'last-seen', - 'role', - 'username' - ] - } - }; - - constructor(listContainerElement, options = {}) { - super(listContainerElement, {...UserList.options, ...options}); - } - - init(users) { - super._init(Object.values(users)); - } - - onClick(event) { - let userElement = event.target.closest('tr'); - if (userElement === null) {return;} - let userId = userElement.dataset.id; - if (userId === undefined) {return;} - let actionButtonElement = event.target.closest('.action-button'); - let action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action; - switch (action) { - case 'delete': { - Utils.deleteUserRequest(userId); - if (userId === currentUserId) {window.location.href = '/';} - break; - } - case 'edit': { - window.location.href = `/admin/users/${userId}/edit`; - break; - } - case 'view': { - window.location.href = `/admin/users/${userId}`; - break; - } - default: { - break; - } - } - } -} diff --git a/app/static/js/Utils.js b/app/static/js/Utils.js index 5d98bcc8..235b4aa7 100644 --- a/app/static/js/Utils.js +++ b/app/static/js/Utils.js @@ -5,6 +5,13 @@ class Utils { return tmpElement.firstChild; } + static generateElementId(prefix='') { + for (let i = 0; true; i++) { + if (document.querySelector(`#${prefix}${i}`) !== null) {continue;} + return `${prefix}${i}`; + } + } + static buildCorpusRequest(userId, corpusId) { return new Promise((resolve, reject) => { let corpus = app.data.users[userId].corpora[corpusId]; diff --git a/app/templates/_scripts.html.j2 b/app/templates/_scripts.html.j2 index d53dea34..9552337f 100644 --- a/app/templates/_scripts.html.j2 +++ b/app/templates/_scripts.html.j2 @@ -19,16 +19,16 @@ 'js/RessourceDisplays/RessourceDisplay.js', 'js/RessourceDisplays/CorpusDisplay.js', 'js/RessourceDisplays/JobDisplay.js', - 'js/RessourceLists/RessourceList.js', - 'js/RessourceLists/CorpusList.js', - 'js/RessourceLists/CorpusFileList.js', - 'js/RessourceLists/JobList.js', - 'js/RessourceLists/JobInputList.js', - 'js/RessourceLists/JobResultList.js', - 'js/RessourceLists/PublicUserList.js', - 'js/RessourceLists/SpacyNLPPipelineModelList.js', - 'js/RessourceLists/TesseractOCRPipelineModelList.js', - 'js/RessourceLists/UserList.js', + 'js/ResourceLists/ResourceList.js', + 'js/ResourceLists/CorpusFileList.js', + 'js/ResourceLists/CorpusList.js', + 'js/ResourceLists/JobList.js', + 'js/ResourceLists/JobInputList.js', + 'js/ResourceLists/JobResultList.js', + 'js/ResourceLists/UserList.js', + 'js/ResourceLists/PublicUserList.js', + 'js/ResourceLists/SpacyNLPPipelineModelList.js', + 'js/ResourceLists/TesseractOCRPipelineModelList.js', 'js/XMLtoObject.js' %} @@ -62,7 +62,7 @@ document.querySelectorAll('#nav-more-dropdown-trigger'), {alignment: 'right', constrainWidth: false, coverTrigger: false} ); - RessourceList.autoInit(); + ResourceList.autoInit(); Form.autoInit(); // Display flashed messages diff --git a/app/templates/admin/users.html.j2 b/app/templates/admin/users.html.j2 index 55c5dc2d..0594b842 100644 --- a/app/templates/admin/users.html.j2 +++ b/app/templates/admin/users.html.j2 @@ -23,6 +23,6 @@ {{ super() }} {% endblock scripts %} diff --git a/app/templates/main/dashboard.html.j2 b/app/templates/main/dashboard.html.j2 index d6624f8a..9b45d337 100644 --- a/app/templates/main/dashboard.html.j2 +++ b/app/templates/main/dashboard.html.j2 @@ -126,6 +126,6 @@ {{ super() }} {% endblock scripts %}