Implementation of DMP finalisation with option to finalise Datasets that belong to the DMP (Ticket #46 - Finalization of DMPs)

This commit is contained in:
Ioannis Kalyvas 2018-10-11 14:22:03 +03:00
parent 1d4eb0128e
commit 2ca29e303b
14 changed files with 216 additions and 37 deletions

View File

@ -32,7 +32,7 @@ import java.util.stream.Collectors;
public class DMP implements DataEntity<DMP, UUID> {
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<DMP, UUID> {
case 0:
return ACTIVE;
case 1:
return FINALISED;
case 99:
return DELETED;
default:
throw new RuntimeException("Unsupported DMP Status");

View File

@ -17,6 +17,8 @@ public interface QueryableList<T extends DataEntity> {
List<T> toList();
<V> void update(EntitySelectPredicate<T> selectPredicate, V value);
QueryableList<T> withFields(List<String> fields);
CompletableFuture<List<T>> toListAsync();

View File

@ -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<T extends DataEntity> implements QueryableLi
this.nestedQueryRoot = subquery.from(this.tClass);
return this;
}
@Override
public <V> void update(EntitySelectPredicate<T> selectPredicate, V value) {
CriteriaBuilder builder = this.manager
.getCriteriaBuilder();
CriteriaUpdate<T> 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();
}
}

View File

@ -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<T> {
Path applyPredicate(Root<T> root);
}

View File

@ -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.<Integer>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());

View File

@ -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<DMP, DataManagementPlan> {
private Tuple<UUID, String> profile;
private int version;
private int status;
private boolean lockable;
private String description;
private List<DatasetListingModel> datasets;
private List<AssociatedProfile> profiles;
private eu.eudat.models.data.project.Project project;
private List<Organisation> organisations;
@ -173,6 +177,22 @@ public class DataManagementPlan implements DataModel<DMP, DataManagementPlan> {
this.dynamicFields = dynamicFields;
}
public boolean getLockable() {
return lockable;
}
public void setLockable(boolean lockable) {
this.lockable = lockable;
}
public List<DatasetListingModel> getDatasets() {
return datasets;
}
public void setDatasets(List<DatasetListingModel> datasets) {
this.datasets = datasets;
}
@Override
public DataManagementPlan fromDataModel(DMP entity) {
this.id = entity.getId();
@ -187,6 +207,7 @@ public class DataManagementPlan implements DataModel<DMP, DataManagementPlan> {
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<DMP, DataManagementPlan> {
}
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;
}

View File

@ -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,

View File

@ -1,17 +1,21 @@
<div class="data-management-plan-editor">
<mat-card-title *ngIf="isNew">{{'DMP-EDITOR.TITLE.NEW' | translate}}</mat-card-title>
<mat-card-title *ngIf="!isNew">
<h3>{{formGroup?.get('label')?.value}} </h3>
</mat-card-title>
<div fxLayout="row" fxLayoutAlign="space-between center">
<mat-card-title *ngIf="isNew">{{'DMP-EDITOR.TITLE.NEW' | translate}}</mat-card-title>
<mat-card-title *ngIf="!isNew">
<h3>{{formGroup?.get('label')?.value}} </h3>
</mat-card-title>
<button *ngIf="!editMode" mat-icon-button (click)="enableForm()">
<mat-icon class="mat-24">edit</mat-icon>
</button>
<button *ngIf="editMode" mat-icon-button (click)="disableForm()">
<mat-icon class="mat-24">lock</mat-icon>
</button>
</div>
<form *ngIf="formGroup" (ngSubmit)="formSubmit()" [formGroup]="formGroup">
<mat-card>
<mat-card-header>
<div class="fill-space"></div>
<div *ngIf="!isNew">
<!-- <div fxFlex>
<button mat-button type="button" (click)="redirectToProject()">
<mat-icon>arrow_right_alt</mat-icon>{{'DMP-EDITOR.ACTIONS.GO-TO-PROJECT' | translate}}</button>
</div> -->
<div>
<button mat-button type="button" (click)="redirectToDatasets()">
<mat-icon>arrow_right_alt</mat-icon>{{'DMP-EDITOR.ACTIONS.GO-TO-DATASETS' | translate}}
@ -127,9 +131,12 @@
<div class="right-button">
<button mat-raised-button color="primary" (click)="cancel()" type="button">{{'DMP-EDITOR.ACTIONS.CANCEL' |
translate}}</button>
<!-- <button *ngIf="!isNew" mat-raised-button color="primary" (click)="invite()" type="button">{{'DMP-EDITOR.ACTIONS.INVITE' | translate}}</button> -->
<button mat-raised-button color="primary" type="submit">{{'DMP-EDITOR.ACTIONS.SAVE' | translate}}</button>
<button *ngIf="!isNew" mat-raised-button color="primary" type="button" (click)="openConfirm(formGroup.get('label').value, formGroup.get('id').value)">{{'DMP-EDITOR.ACTIONS.DELETE'
<button *ngIf="dataManagementPlan.status != 1" mat-raised-button color="primary" type="submit">{{'DMP-EDITOR.ACTIONS.SAVE'
| translate}}</button>
<button *ngIf="dataManagementPlan.lockable && dataManagementPlan.status != 1" type="button" mat-raised-button
color="primary" (click)="saveAndFinalize()">{{'DMP-EDITOR.ACTIONS.FINALISE'
| translate}}</button>
<button *ngIf="!isNew && dataManagementPlan.status != 1" mat-raised-button color="primary" type="button" (click)="openConfirm(formGroup.get('label').value, formGroup.get('id').value)">{{'DMP-EDITOR.ACTIONS.DELETE'
| translate}}</button>
</div>
</div>

View File

@ -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<BreadcrumbItem[]>;
isNew = true;
@ -92,7 +95,7 @@ export class DataManagementPlanEditorComponent implements AfterViewInit, IBreadC
projectRequestItem.criteria = new ProjectCriteria();
const organisationRequestItem: RequestItem<BaseCriteria> = 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
}
});
}
}

View File

@ -0,0 +1,25 @@
<form>
<div mat-dialog-title>
</div>
<div mat-dialog-content *ngIf="datasetsFinalised && datasetsDraft">
{{'DMP-FINALISE-DIALOG.ALREADY-FINALISED-DATASETS' | translate}}
<mat-list>
<mat-list-item *ngFor="let dataset of datasetsFinalised | async; let len = length">
<div>{{ dataset.label }}</div>
</mat-list-item>
</mat-list>
{{'DMP-FINALISE-DIALOG.FINALISE-TITLE' | translate}}
<mat-selection-list #datasetsDraftSelectionList>
<mat-list-option *ngFor="let dataset of datasetsDraft | async; let len = length" [value]='dataset'>
{{ dataset.label }}
</mat-list-option>
</mat-selection-list>
</div>
<div mat-dialog-actions>
<div class="full-width">
<button mat-raised-button (click)="submit()">
{{'DMP-FINALISE-DIALOG.SUBMIT' | translate}}
</button>
</div>
</div>
</form>

View File

@ -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<DatasetListingModel[]>;
public datasetsDraft: Observable<DatasetListingModel[]>;
constructor(
public router: Router,
public dialogRef: MatDialogRef<DMPFinaliseDialogComponent>,
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<DatasetCriteria>(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<DatasetCriteria>(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));
}
}

View File

@ -16,15 +16,11 @@
<mat-error *ngIf="formGroup.get('description').errors?.required">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
<div class="col-md-6">
<app-single-auto-complete [reactiveFormControl]="formGroup.get('project')" placeholder="{{this.languageResolverService.getBy('dmpEditor') | translate}}"
[configuration]="projectAutoCompleteConfiguration">
</app-single-auto-complete>
</div>
<!-- <app-dynamic-fields-project [formGroup]="formGroup"></app-dynamic-fields-project> -->
<div class="col-md-6">
<div class="row">
<app-multiple-auto-complete class="col-md-10" [reactiveFormControl]="formGroup.get('profiles')" placeholder="{{'DMP-EDITOR.FIELDS.PROFILES' | translate}}"
@ -37,9 +33,6 @@
</div>
</div>
</div>
<!-- <button mat-button (click)="availableProfiles()">View All</button> -->
<div class="col-md-6">
<app-multiple-auto-complete [reactiveFormControl]="formGroup.get('organisations')" placeholder="{{'DMP-EDITOR.FIELDS.ORGANISATIONS' | translate}}"
[configuration]="organisationsAutoCompleteConfiguration">

View File

@ -20,6 +20,7 @@ export class DataManagementPlanModel implements Serializable<DataManagementPlanM
public groupId: String;
public profile: String;
public version: number;
public lockable: boolean;
public creator: DmpUsersModel;
public status: Status = Status.Active;
public description: String;
@ -39,6 +40,7 @@ export class DataManagementPlanModel implements Serializable<DataManagementPlanM
this.groupId = item.groupId;
this.version = item.version;
this.status = item.status;
this.lockable = item.lockable;
this.description = item.description;
this.project = JsonSerializer.fromJSONObject(item.project, ProjectModel);
this.organisations = JsonSerializer.fromJSONArray(item.organisations, OrganisationModel);