From 2ca29e303b821ffe44514cf855208b7e831bd856 Mon Sep 17 00:00:00 2001 From: Ioannis Kalyvas Date: Thu, 11 Oct 2018 14:22:03 +0300 Subject: [PATCH] Implementation of DMP finalisation with option to finalise Datasets that belong to the DMP (Ticket #46 - Finalization of DMPs) --- .../main/java/eu/eudat/data/entities/DMP.java | 4 +- .../eu/eudat/queryable/QueryableList.java | 2 + .../QueryableHibernateList.java | 19 ++++-- .../jpa/predicates/EntitySelectPredicate.java | 13 ++++ .../managers/DataManagementPlanManager.java | 10 ++- .../models/data/dmp/DataManagementPlan.java | 22 +++++++ dmp-frontend/src/app/dmps/dmps.module.ts | 10 ++- .../app/dmps/editor/dmp-editor.component.html | 29 +++++---- .../app/dmps/editor/dmp-editor.component.ts | 46 ++++++++++--- .../dmp-finalise-dialog.component.html | 25 ++++++++ .../dmp-finalise-dialog.component.scss | 0 .../dmp-finalise-dialog.component.ts | 64 +++++++++++++++++++ .../editor/dmp-wizard-editor.component.html | 7 -- .../DataManagementPlanModel.ts | 2 + 14 files changed, 216 insertions(+), 37 deletions(-) create mode 100644 dmp-backend/queryable/src/main/java/eu/eudat/queryable/jpa/predicates/EntitySelectPredicate.java create mode 100644 dmp-frontend/src/app/dmps/editor/dmp-finalise-dialog/dmp-finalise-dialog.component.html create mode 100644 dmp-frontend/src/app/dmps/editor/dmp-finalise-dialog/dmp-finalise-dialog.component.scss create mode 100644 dmp-frontend/src/app/dmps/editor/dmp-finalise-dialog/dmp-finalise-dialog.component.ts diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/entities/DMP.java b/dmp-backend/data/src/main/java/eu/eudat/data/entities/DMP.java index 3ed583f89..a91f944dd 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/entities/DMP.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/entities/DMP.java @@ -32,7 +32,7 @@ import java.util.stream.Collectors; public class DMP implements DataEntity { public enum DMPStatus { - ACTIVE((short) 0), DELETED((short) 1); + ACTIVE((short) 0), FINALISED((short) 1),DELETED((short) 99); private short value; @@ -49,6 +49,8 @@ public class DMP implements DataEntity { case 0: return ACTIVE; case 1: + return FINALISED; + case 99: return DELETED; default: throw new RuntimeException("Unsupported DMP Status"); diff --git a/dmp-backend/queryable/src/main/java/eu/eudat/queryable/QueryableList.java b/dmp-backend/queryable/src/main/java/eu/eudat/queryable/QueryableList.java index a3656d7a3..4f8f05695 100644 --- a/dmp-backend/queryable/src/main/java/eu/eudat/queryable/QueryableList.java +++ b/dmp-backend/queryable/src/main/java/eu/eudat/queryable/QueryableList.java @@ -17,6 +17,8 @@ public interface QueryableList { List toList(); + void update(EntitySelectPredicate selectPredicate, V value); + QueryableList withFields(List fields); CompletableFuture> toListAsync(); diff --git a/dmp-backend/queryable/src/main/java/eu/eudat/queryable/jpa/hibernatequeryablelist/QueryableHibernateList.java b/dmp-backend/queryable/src/main/java/eu/eudat/queryable/jpa/hibernatequeryablelist/QueryableHibernateList.java index e46453e45..71e1331dd 100644 --- a/dmp-backend/queryable/src/main/java/eu/eudat/queryable/jpa/hibernatequeryablelist/QueryableHibernateList.java +++ b/dmp-backend/queryable/src/main/java/eu/eudat/queryable/jpa/hibernatequeryablelist/QueryableHibernateList.java @@ -2,10 +2,7 @@ package eu.eudat.queryable.jpa.hibernatequeryablelist; import eu.eudat.queryable.QueryableList; import eu.eudat.queryable.exceptions.NotSingleResultException; -import eu.eudat.queryable.jpa.predicates.NestedQuerySinglePredicate; -import eu.eudat.queryable.jpa.predicates.OrderByPredicate; -import eu.eudat.queryable.jpa.predicates.SelectPredicate; -import eu.eudat.queryable.jpa.predicates.SinglePredicate; +import eu.eudat.queryable.jpa.predicates.*; import eu.eudat.queryable.queryableentity.DataEntity; import eu.eudat.queryable.types.FieldSelectionType; import eu.eudat.queryable.types.SelectionField; @@ -408,4 +405,18 @@ public class QueryableHibernateList implements QueryableLi this.nestedQueryRoot = subquery.from(this.tClass); return this; } + + @Override + public void update(EntitySelectPredicate selectPredicate, V value) { + CriteriaBuilder builder = this.manager + .getCriteriaBuilder(); + CriteriaUpdate update = builder + .createCriteriaUpdate(tClass); + this.root = update.from(tClass); + update.set(selectPredicate.applyPredicate(root), value) + .where(this.generateWherePredicates(this.singlePredicates, this.root, this.nestedPredicates, this.nestedQueryRoot)); + this.manager + .createQuery(update) + .executeUpdate(); + } } diff --git a/dmp-backend/queryable/src/main/java/eu/eudat/queryable/jpa/predicates/EntitySelectPredicate.java b/dmp-backend/queryable/src/main/java/eu/eudat/queryable/jpa/predicates/EntitySelectPredicate.java new file mode 100644 index 000000000..c6c3cb7b7 --- /dev/null +++ b/dmp-backend/queryable/src/main/java/eu/eudat/queryable/jpa/predicates/EntitySelectPredicate.java @@ -0,0 +1,13 @@ +package eu.eudat.queryable.jpa.predicates; + +import javax.persistence.criteria.Path; +import javax.persistence.criteria.Root; +import java.util.function.Predicate; + +/** + * Created by ikalyvas on 10/10/2018. + */ +public interface EntitySelectPredicate { + Path applyPredicate(Root root); + +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementPlanManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementPlanManager.java index b34ba9bf4..1df23a2d9 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementPlanManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementPlanManager.java @@ -160,6 +160,12 @@ public class DataManagementPlanManager { createProjectIfItDoesntExist(newDmp, apiContext.getOperationsContext().getDatabaseRepository().getProjectDao(), user); newDmp.setCreator(user); newDmp = apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().createOrUpdate(newDmp); + if (dataManagementPlan.getStatus() == DMP.DMPStatus.FINALISED.getValue()) { + apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao() + .asQueryable().where((builder, root) -> root.get("id").in(dataManagementPlan.getDatasets() + .stream().map(x -> UUID.fromString(x.getId())).collect(Collectors.toList()))) + .update(root -> root.get("status"), Dataset.Status.FINALISED.getValue()); + } if (dataManagementPlan.getAssociatedUsers().stream().filter(item -> item.getId() == principal.getId()).collect(Collectors.toList()).size() == 0) assignUser(newDmp, user, apiContext); } @@ -324,7 +330,7 @@ public class DataManagementPlanManager { projectName.setTextContent(dmp.getProject().getLabel()); dmpElement.appendChild(projectName); Element organisationsElement = xmlDoc.createElement("organisations"); - for(Organisation organisation : dmp.getOrganisations()){ + for (Organisation organisation : dmp.getOrganisations()) { Element organisationElement = xmlDoc.createElement("organisation"); organisationElement.setAttribute("name", organisation.getLabel()); organisationElement.setAttribute("reference", organisation.getReference()); @@ -333,7 +339,7 @@ public class DataManagementPlanManager { dmpElement.appendChild(organisationsElement); Element researchersElement = xmlDoc.createElement("researchers"); - for(Researcher researcher : dmp.getResearchers()){ + for (Researcher researcher : dmp.getResearchers()) { Element researcherElement = xmlDoc.createElement("organisation"); researcherElement.setAttribute("name", researcher.getLabel()); researcherElement.setAttribute("reference", researcher.getReference()); diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/dmp/DataManagementPlan.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/dmp/DataManagementPlan.java index 8cfab229f..a96fc00e6 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/dmp/DataManagementPlan.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/dmp/DataManagementPlan.java @@ -2,11 +2,13 @@ package eu.eudat.models.data.dmp; import eu.eudat.data.entities.DMP; import eu.eudat.data.entities.DMPProfile; +import eu.eudat.data.entities.Dataset; import eu.eudat.logic.utilities.builders.XmlBuilder; import eu.eudat.models.DataModel; import eu.eudat.models.data.dynamicfields.DynamicFieldWithValue; import eu.eudat.models.data.entities.xmlmodels.dmpprofiledefinition.DataManagementPlanProfile; import eu.eudat.models.data.helpermodels.Tuple; +import eu.eudat.models.data.listingmodels.DatasetListingModel; import eu.eudat.models.data.project.Project; import eu.eudat.models.data.userinfo.UserInfo; import net.minidev.json.JSONObject; @@ -25,7 +27,9 @@ public class DataManagementPlan implements DataModel { private Tuple profile; private int version; private int status; + private boolean lockable; private String description; + private List datasets; private List profiles; private eu.eudat.models.data.project.Project project; private List organisations; @@ -173,6 +177,22 @@ public class DataManagementPlan implements DataModel { this.dynamicFields = dynamicFields; } + public boolean getLockable() { + return lockable; + } + + public void setLockable(boolean lockable) { + this.lockable = lockable; + } + + public List getDatasets() { + return datasets; + } + + public void setDatasets(List datasets) { + this.datasets = datasets; + } + @Override public DataManagementPlan fromDataModel(DMP entity) { this.id = entity.getId(); @@ -187,6 +207,7 @@ public class DataManagementPlan implements DataModel { this.project.fromDataModel(entity.getProject()); this.creator = new eu.eudat.models.data.userinfo.UserInfo(); this.groupId = entity.getGroupId(); + this.lockable = entity.getDataset().stream().findAny().isPresent(); this.definition = entity.getProfile() == null ? null : new DataManagementPlanProfile().fromXml(XmlBuilder.fromXml(entity.getProfile().getDefinition()).getDocumentElement()); if (this.definition != null && this.definition.getFields() != null && !this.definition.getFields().isEmpty() && this.properties != null) { this.definition.getFields().forEach(item -> { @@ -212,6 +233,7 @@ public class DataManagementPlan implements DataModel { } this.created = entity.getCreated(); this.description = entity.getDescription(); + this.status = entity.getStatus(); this.associatedUsers = entity.getUsers().stream().map(item -> new UserInfo().fromDataModel(item)).collect(Collectors.toList()); return this; } diff --git a/dmp-frontend/src/app/dmps/dmps.module.ts b/dmp-frontend/src/app/dmps/dmps.module.ts index 7e0244f97..8ef282837 100644 --- a/dmp-frontend/src/app/dmps/dmps.module.ts +++ b/dmp-frontend/src/app/dmps/dmps.module.ts @@ -32,6 +32,7 @@ import { ResearcherService } from '../services/researchers/researchers.service'; import { InvitationService } from '../services/invitation/invitation.service'; import { DatasetService } from '../services/dataset/dataset.service'; import { FlexLayoutModule } from '@angular/flex-layout'; +import { DMPFinaliseDialogComponent } from './editor/dmp-finalise-dialog/dmp-finalise-dialog.component'; @NgModule({ imports: [ @@ -64,7 +65,8 @@ import { FlexLayoutModule } from '@angular/flex-layout'; AvailableProfilesComponent, DynamicDmpFieldResolverComponent, DynamicFieldsProjectComponent, - DynamicFieldProjectComponent + DynamicFieldProjectComponent, + DMPFinaliseDialogComponent ], exports: [ @@ -79,12 +81,14 @@ import { FlexLayoutModule } from '@angular/flex-layout'; AvailableProfilesComponent, DynamicFieldsProjectComponent, DynamicFieldProjectComponent, - RouterModule + RouterModule, + DMPFinaliseDialogComponent ], entryComponents: [ InvitationComponent, AddResearchersComponent, - AvailableProfilesComponent + AvailableProfilesComponent, + DMPFinaliseDialogComponent ], providers: [ ProjectService, diff --git a/dmp-frontend/src/app/dmps/editor/dmp-editor.component.html b/dmp-frontend/src/app/dmps/editor/dmp-editor.component.html index 25728e09e..b14a0eb07 100644 --- a/dmp-frontend/src/app/dmps/editor/dmp-editor.component.html +++ b/dmp-frontend/src/app/dmps/editor/dmp-editor.component.html @@ -1,17 +1,21 @@
- {{'DMP-EDITOR.TITLE.NEW' | translate}} - -

{{formGroup?.get('label')?.value}}

-
+
+ {{'DMP-EDITOR.TITLE.NEW' | translate}} + +

{{formGroup?.get('label')?.value}}

+
+ + +
-
- - - + +
diff --git a/dmp-frontend/src/app/dmps/editor/dmp-editor.component.ts b/dmp-frontend/src/app/dmps/editor/dmp-editor.component.ts index d9f2f5a09..e8f56c451 100644 --- a/dmp-frontend/src/app/dmps/editor/dmp-editor.component.ts +++ b/dmp-frontend/src/app/dmps/editor/dmp-editor.component.ts @@ -34,6 +34,8 @@ import { BreadcrumbItem } from '../../shared/components/breadcrumb/definition/br import { SingleAutoCompleteConfiguration } from '../../shared/components/autocompletes/single/single-auto-complete-configuration'; import { MultipleAutoCompleteConfiguration } from '../../shared/components/autocompletes/multiple/multiple-auto-complete-configuration'; import * as FileSaver from 'file-saver'; +import { DMPFinaliseDialogComponent } from './dmp-finalise-dialog/dmp-finalise-dialog.component'; +import { DatasetListingModel } from '../../models/datasets/DatasetListingModel'; @Component({ selector: 'app-dmp-editor-component', @@ -43,6 +45,7 @@ import * as FileSaver from 'file-saver'; }) export class DataManagementPlanEditorComponent implements AfterViewInit, IBreadCrumbComponent { + editMode = true; breadCrumbs: Observable; isNew = true; @@ -92,7 +95,7 @@ export class DataManagementPlanEditorComponent implements AfterViewInit, IBreadC projectRequestItem.criteria = new ProjectCriteria(); const organisationRequestItem: RequestItem = new RequestItem(); organisationRequestItem.criteria = new BaseCriteria(); - //this.projectAutoCompleteConfiguration = new AutoCompleteConfiguration(this.projectService.getWithExternal.bind(this.projectService), projectRequestItem); + this.projectAutoCompleteConfiguration = { @@ -135,6 +138,8 @@ export class DataManagementPlanEditorComponent implements AfterViewInit, IBreadC .subscribe(async data => { this.dataManagementPlan = JsonSerializer.fromJSONObject(data, DataManagementPlanModel); this.formGroup = this.dataManagementPlan.buildForm(); + this.editMode = this.dataManagementPlan.status !== 1; + if (!this.editMode) { this.formGroup.disable(); } if (this.formGroup.get('profile') && this.formGroup.get('profile').value) { this.textCtrl.patchValue(this.formGroup.get('profile').value); } @@ -144,8 +149,7 @@ export class DataManagementPlanEditorComponent implements AfterViewInit, IBreadC label: 'DMPs', url: 'dmps', notFoundResolver: [await this.projectService.getSingle(this.dataManagementPlan.project.id).map(x => ({ label: x.label, url: '/projects/edit/' + x.id }) as BreadcrumbItem).toPromise()] - } - ] + }] ); this.associatedUsers = data.associatedUsers; }); @@ -280,12 +284,11 @@ export class DataManagementPlanEditorComponent implements AfterViewInit, IBreadC openConfirm(dmpLabel, id): void { this._dialogService.openConfirm({ message: 'Are you sure you want to delete the "' + dmpLabel + '"', - disableClose: true || false, // defaults to false - viewContainerRef: this._viewContainerRef, //OPTIONAL - title: 'Confirm', //OPTIONAL, hides if not provided - cancelButton: 'No', //OPTIONAL, defaults to 'CANCEL' - acceptButton: 'Yes' //OPTIONAL, defaults to 'ACCEPT' - // width: '500px', //OPTIONAL, defaults to 400px + disableClose: true || false, + viewContainerRef: this._viewContainerRef, + title: 'Confirm', + cancelButton: 'No', + acceptButton: 'Yes' }).afterClosed().subscribe((accept: boolean) => { if (accept) { this.dataManagementPlanService.delete(id).subscribe(() => { @@ -326,6 +329,7 @@ export class DataManagementPlanEditorComponent implements AfterViewInit, IBreadC clone(id: String) { this.router.navigate(['/dmps/clone/' + id]); } + viewVersions(rowId: String, rowLabel: String) { this.router.navigate(['/dmps/viewversions/' + rowId], { queryParams: { groupLabel: rowLabel } }); } @@ -356,4 +360,28 @@ export class DataManagementPlanEditorComponent implements AfterViewInit, IBreadC } return filename; } + + public enableForm() { + this.editMode = true; + this.formGroup.enable(); + } + + public disableForm() { + this.editMode = false; + this.formGroup.disable(); + } + + saveAndFinalize() { + const dialogRef = this.dialog.open(DMPFinaliseDialogComponent, { + data: { + submitFunction: (items: DatasetListingModel[]) => { + this.formGroup.get('status').setValue('1'); + this.formGroup.addControl('datasets', new FormControl(items)); + this.formSubmit(); + dialogRef.close(); + }, + dmp: this.dataManagementPlan + } + }); + } } diff --git a/dmp-frontend/src/app/dmps/editor/dmp-finalise-dialog/dmp-finalise-dialog.component.html b/dmp-frontend/src/app/dmps/editor/dmp-finalise-dialog/dmp-finalise-dialog.component.html new file mode 100644 index 000000000..b1f871734 --- /dev/null +++ b/dmp-frontend/src/app/dmps/editor/dmp-finalise-dialog/dmp-finalise-dialog.component.html @@ -0,0 +1,25 @@ + +
+
+
+ {{'DMP-FINALISE-DIALOG.ALREADY-FINALISED-DATASETS' | translate}} + + +
{{ dataset.label }}
+
+
+ {{'DMP-FINALISE-DIALOG.FINALISE-TITLE' | translate}} + + + {{ dataset.label }} + + +
+
+
+ +
+
+ diff --git a/dmp-frontend/src/app/dmps/editor/dmp-finalise-dialog/dmp-finalise-dialog.component.scss b/dmp-frontend/src/app/dmps/editor/dmp-finalise-dialog/dmp-finalise-dialog.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/dmp-frontend/src/app/dmps/editor/dmp-finalise-dialog/dmp-finalise-dialog.component.ts b/dmp-frontend/src/app/dmps/editor/dmp-finalise-dialog/dmp-finalise-dialog.component.ts new file mode 100644 index 000000000..dde7cfde4 --- /dev/null +++ b/dmp-frontend/src/app/dmps/editor/dmp-finalise-dialog/dmp-finalise-dialog.component.ts @@ -0,0 +1,64 @@ +import { Component, OnInit, Inject, ViewChild } from '@angular/core'; +import { FormGroup } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; +import { AuthService } from '../../../services/auth/auth.service'; +import { MatDialogRef, MAT_DIALOG_DATA, MatSelectionList } from '@angular/material'; +import { DataManagementPlanModel } from '../../../models/data-managemnt-plans/DataManagementPlanModel'; +import { DataManagementPlanService } from '../../../services/data-management-plan/data-management-plan.service'; +import { DatasetListingModel } from '../../../models/datasets/DatasetListingModel'; +import { DatasetService } from '../../../services/dataset/dataset.service'; +import { DataTableRequest } from '../../../models/data-table/DataTableRequest'; +import { DatasetCriteria } from '../../../models/criteria/dataset/DatasetCriteria'; +import { DatasetStatus } from '../../../models/datasets/DatasetWizardModel'; +import { Observable } from 'rxjs/Observable'; + +@Component({ + selector: 'app-dmp-finalise-dialog-component', + templateUrl: 'dmp-finalise-dialog.component.html', + +}) +export class DMPFinaliseDialogComponent implements OnInit { + + @ViewChild('datasetsDraftSelectionList') selectionList: MatSelectionList; + public formGroup: FormGroup; + public submitFunction: (items: DatasetListingModel[]) => any; + public dmp: DataManagementPlanModel; + public datasetsFinalised: Observable; + public datasetsDraft: Observable; + constructor( + public router: Router, + public dialogRef: MatDialogRef, + public dmpService: DatasetService, + @Inject(MAT_DIALOG_DATA) public data: any + ) { + this.submitFunction = data['submitFunction']; + this.dmp = data['dmp']; + this.initialiseDatasetFinalisedRequest(); + this.initialiseDMPFinalisedRequest(); + } + + ngOnInit(): void { + + } + + initialiseDatasetFinalisedRequest() { + const request = new DataTableRequest(null, null, null); + request.criteria = new DatasetCriteria(); + request.criteria.dmpIds = [this.dmp.id]; + request.criteria.status = DatasetStatus.Finalised; + this.datasetsFinalised = this.dmpService.getPaged(request).map(x => x.data); + } + + + initialiseDMPFinalisedRequest() { + const request = new DataTableRequest(null, null, null); + request.criteria = new DatasetCriteria(); + request.criteria.dmpIds = [this.dmp.id]; + request.criteria.status = DatasetStatus.Draft; + this.datasetsDraft = this.dmpService.getPaged(request).map(x => x.data); + } + + submit() { + this.submitFunction(this.selectionList.selectedOptions.selected.map(x => x.value)); + } +} diff --git a/dmp-frontend/src/app/dmps/wizard/editor/dmp-wizard-editor.component.html b/dmp-frontend/src/app/dmps/wizard/editor/dmp-wizard-editor.component.html index 420b2fbc6..251a97d99 100644 --- a/dmp-frontend/src/app/dmps/wizard/editor/dmp-wizard-editor.component.html +++ b/dmp-frontend/src/app/dmps/wizard/editor/dmp-wizard-editor.component.html @@ -16,15 +16,11 @@ {{'GENERAL.VALIDATION.REQUIRED' | translate}} - - -
-
- - -
diff --git a/dmp-frontend/src/app/models/data-managemnt-plans/DataManagementPlanModel.ts b/dmp-frontend/src/app/models/data-managemnt-plans/DataManagementPlanModel.ts index 81b9d5153..97a20b11b 100644 --- a/dmp-frontend/src/app/models/data-managemnt-plans/DataManagementPlanModel.ts +++ b/dmp-frontend/src/app/models/data-managemnt-plans/DataManagementPlanModel.ts @@ -20,6 +20,7 @@ export class DataManagementPlanModel implements Serializable