Add DMP Zenodo license field (ref #274)

This commit is contained in:
George Kalampokis 2020-06-26 11:46:18 +03:00
parent d81d6e8568
commit b0dddeeb51
15 changed files with 361 additions and 7 deletions

View File

@ -41,6 +41,7 @@ public class ResponsesCache {
caches.add(new GuavaCache("researchers", CacheBuilder.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build()));
caches.add(new GuavaCache("externalDatasets", CacheBuilder.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build()));
caches.add(new GuavaCache("currencies", CacheBuilder.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build()));
caches.add(new GuavaCache("licenses", CacheBuilder.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build()));
simpleCacheManager.setCaches(caches);
logger.info("OK");
return simpleCacheManager;

View File

@ -0,0 +1,41 @@
package eu.eudat.controllers;
import eu.eudat.logic.managers.LicenseManager;
import eu.eudat.logic.proxy.config.exceptions.HugeResultSet;
import eu.eudat.logic.proxy.config.exceptions.NoURLFound;
import eu.eudat.logic.services.ApiContext;
import eu.eudat.models.data.helpers.responses.ResponseItem;
import eu.eudat.models.data.license.LicenseModel;
import eu.eudat.models.data.security.Principal;
import eu.eudat.types.ApiMessageCode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@CrossOrigin
@RequestMapping(value = {"/api/external/licenses"})
public class Licenses extends BaseController {
private LicenseManager licenseManager;
@Autowired
public Licenses(ApiContext apiContext, LicenseManager licenseManager) {
super(apiContext);
this.licenseManager = licenseManager;
}
@RequestMapping(method = RequestMethod.GET, produces = "application/json")
public @ResponseBody
ResponseEntity<ResponseItem<List<LicenseModel>>> listExternalLicenses(
@RequestParam(value = "query", required = false) String query, @RequestParam(value = "type", required = false) String type, Principal principal
) throws HugeResultSet, NoURLFound {
List<LicenseModel> licenseModels = this.licenseManager.getLicenses(query, type);
return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<List<LicenseModel>>().status(ApiMessageCode.NO_MESSAGE).payload(licenseModels));
}
}

View File

@ -1,6 +1,5 @@
package eu.eudat.logic.managers;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import eu.eudat.configurations.dynamicgrant.DynamicGrantConfiguration;
@ -14,10 +13,8 @@ import eu.eudat.data.enumeration.notification.ActiveStatus;
import eu.eudat.data.enumeration.notification.ContactType;
import eu.eudat.data.enumeration.notification.NotificationType;
import eu.eudat.data.enumeration.notification.NotifyState;
import eu.eudat.data.query.items.item.dmp.DataManagementPlanCriteriaRequest;
import eu.eudat.data.query.items.table.datasetprofile.DatasetProfileTableRequestItem;
import eu.eudat.data.query.items.table.dmp.DataManagementPlanTableRequest;
import eu.eudat.data.query.items.table.dmp.DataManagmentPlanPublicTableRequest;
import eu.eudat.elastic.criteria.DmpCriteria;
import eu.eudat.elastic.entities.Collaborator;
import eu.eudat.elastic.entities.Dmp;
@ -83,8 +80,6 @@ import org.springframework.web.multipart.MultipartFile;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
@ -1650,6 +1645,7 @@ public class DataManagementPlanManager {
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.setContentType(MediaType.APPLICATION_JSON);
String createData = null;
Map<String, Object> extraProperties = dmp.getExtraProperties() != null ? new org.json.JSONObject(dmp.getExtraProperties()).toMap() : new HashMap<>();
StringBuilder dataBuilder = new StringBuilder();
dataBuilder.append("{\n \"metadata\": {\n");
dataBuilder.append( " \"title\": \"").append(dmp.getLabel()).append("\",\n");
@ -1663,6 +1659,9 @@ public class DataManagementPlanManager {
dataBuilder.append(" \"related_identifiers\": [{\n");
dataBuilder.append(" \t\t\"identifier\": \"").append((this.environment.getProperty("dmp.domain") + "/external/zenodo/" + id.toString())).append("\",\n");
dataBuilder.append(" \t\t\"relation\": \"isIdenticalTo\"}],\n");
if (extraProperties.get("license") != null) {
dataBuilder.append(" \"license\": \"").append(((Map)extraProperties.get("license")).get("pid")).append("\",\n");
}
} else {
dataBuilder.append("restricted\",\n");
dataBuilder.append(" \"access_conditions\": \"\",\n");

View File

@ -0,0 +1,48 @@
package eu.eudat.logic.managers;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import eu.eudat.data.dao.criteria.DataRepositoryCriteria;
import eu.eudat.data.entities.DataRepository;
import eu.eudat.logic.proxy.config.ExternalUrlCriteria;
import eu.eudat.logic.proxy.config.exceptions.HugeResultSet;
import eu.eudat.logic.proxy.config.exceptions.NoURLFound;
import eu.eudat.logic.services.ApiContext;
import eu.eudat.models.data.datarepository.DataRepositoryModel;
import eu.eudat.models.data.license.LicenseModel;
import eu.eudat.models.data.security.Principal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Created by ikalyvas on 9/3/2018.
*/
@Component
public class LicenseManager {
private ApiContext apiContext;
@Autowired
public LicenseManager(ApiContext apiContext) {
this.apiContext = apiContext;
}
public List<LicenseModel> getLicenses(String query, String type) throws HugeResultSet, NoURLFound {
ExternalUrlCriteria externalUrlCriteria = new ExternalUrlCriteria(query);
List<Map<String, String>> remoteRepos = this.apiContext.getOperationsContext().getRemoteFetcher().getlicenses(externalUrlCriteria, type);
DataRepositoryCriteria criteria = new DataRepositoryCriteria();
if (!query.isEmpty()) criteria.setLike(query);
List<LicenseModel> licenseModels = new LinkedList<>();
ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
licenseModels.addAll(remoteRepos.stream().map(item -> mapper.convertValue(item, LicenseModel.class)).collect(Collectors.toList()));
licenseModels = licenseModels.stream().filter(licenseModel -> licenseModel.getName().contains(query)).collect(Collectors.toList());
return licenseModels;
}
}

View File

@ -24,6 +24,7 @@ public class ExternalUrls implements Serializable {
DatasetUrls datasets;
/*TagUrls tags;*/
FunderUrls funders;
LicenseUrls licenses;
public RegistryUrls getRegistries() {
@ -133,6 +134,15 @@ public class ExternalUrls implements Serializable {
public void setDatasets(DatasetUrls datasets) {
this.datasets = datasets;
}
public LicenseUrls getLicenses() {
return licenses;
}
@XmlElement(name = "licenses")
public void setLicenses(LicenseUrls licenses) {
this.licenses = licenses;
}
}

View File

@ -0,0 +1,33 @@
package eu.eudat.logic.proxy.config.entities;
import eu.eudat.logic.proxy.config.FetchStrategy;
import eu.eudat.logic.proxy.config.UrlConfiguration;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import java.util.List;
public class LicenseUrls {
List<UrlConfiguration> urls;
FetchStrategy fetchMode;
public List<UrlConfiguration> getUrls() {
return urls;
}
@XmlElementWrapper
@XmlElement(name = "urlConfig")
public void setUrls(List<UrlConfiguration> urls) {
this.urls = urls;
}
public FetchStrategy getFetchMode() {
return fetchMode;
}
@XmlElement(name = "fetchMode")
public void setFetchMode(FetchStrategy fetchMode) {
this.fetchMode = fetchMode;
}
}

View File

@ -126,6 +126,15 @@ public class RemoteFetcher {
return getAll(urlConfigs, fetchStrategy, externalUrlCriteria);
}
@Cacheable("licenses")
public List<Map<String, String>> getlicenses(ExternalUrlCriteria externalUrlCriteria, String key) throws NoURLFound, HugeResultSet {
List<UrlConfiguration> urlConfigs =
key != null && !key.isEmpty() ? configLoader.getExternalUrls().getLicenses().getUrls().stream().filter(item -> item.getKey().equals(key)).collect(Collectors.toList())
: configLoader.getExternalUrls().getLicenses().getUrls();
FetchStrategy fetchStrategy = configLoader.getExternalUrls().getLicenses().getFetchMode();
return getAll(urlConfigs, fetchStrategy, externalUrlCriteria);
}
private List<Map<String, String>> getAll(List<UrlConfiguration> urlConfigs, FetchStrategy fetchStrategy, ExternalUrlCriteria externalUrlCriteria) throws NoURLFound, HugeResultSet {

View File

@ -0,0 +1,19 @@
package eu.eudat.models.data.external;
import java.util.List;
import java.util.Map;
public class LicensesExternalSourcesModel extends ExternalListingItem<LicensesExternalSourcesModel> {
@Override
public LicensesExternalSourcesModel fromExternalItem(List<Map<String, String>> values) {
for (Map<String, String> item : values) {
ExternalSourcesItemModel model = new ExternalSourcesItemModel();
model.setId(item.get("id"));
model.setUri(item.get("uri"));
model.setName(item.get("name"));
this.add(model);
}
return this;
}
}

View File

@ -0,0 +1,127 @@
package eu.eudat.models.data.license;
import eu.eudat.data.entities.DataRepository;
import eu.eudat.data.entities.UserInfo;
import eu.eudat.models.DataModel;
import java.util.Date;
import java.util.UUID;
public class LicenseModel implements DataModel<DataRepository, LicenseModel> {
private UUID id;
private String name;
private String pid;
private String abbreviation;
private String uri;
private Date created;
private Date modified;
private String tag; // Api fetching the data
private String source; // Actual harvested source
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPid() {
return pid;
}
public void setPid(String pid) {
this.pid = pid;
}
public String getAbbreviation() {
return abbreviation;
}
public void setAbbreviation(String abbreviation) {
this.abbreviation = abbreviation;
}
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public Date getModified() {
return modified;
}
public void setModified(Date modified) {
this.modified = modified;
}
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
@Override
public LicenseModel fromDataModel(DataRepository entity) {
this.setAbbreviation(entity.getAbbreviation());
this.setName(entity.getLabel());
this.setUri(entity.getUri());
this.setId(entity.getId());
this.setPid(entity.getReference());
String source1 = entity.getReference().substring(0, entity.getReference().indexOf(":"));
if (source1.equals("dmp")) {
this.source = "Internal";
} else {
this.source = source1;
}
return this;
}
@Override
public DataRepository toDataModel() throws Exception {
DataRepository dataRepository = new DataRepository();
dataRepository.setId(this.id != null ? this.id : UUID.randomUUID());
dataRepository.setAbbreviation(this.abbreviation);
dataRepository.setCreated(this.created != null ? this.created : new Date());
dataRepository.setModified(new Date());
dataRepository.setLabel(this.name);
if (this.source != null) {
if (this.source.equals("Internal") || this.source.equals(this.id.toString().substring(0, this.source.length()))) {
dataRepository.setReference(this.id.toString());
} else {
dataRepository.setReference(this.source + ":" + dataRepository.getId());
}
} else {
dataRepository.setReference("dmp:" + dataRepository.getId());
}
dataRepository.setUri(this.uri);
dataRepository.setStatus((short) 0);
dataRepository.setCreationUser(new UserInfo());
return dataRepository;
}
@Override
public String getHint() {
return null;
}
}

View File

@ -937,6 +937,30 @@
<fetchMode>FIRST</fetchMode> <!-- EITHER 'FIRST' OR 'ALL' -->
</datasets>
<licenses>
<urls>
<urlConfig>
<key>opendefinition</key>
<label>Open Definition</label>
<ordinal>1</ordinal>
<type>External</type>
<url>https://licenses.opendefinition.org/licenses/groups/all.json</url>
<firstPage>1</firstPage>
<contenttype>application/vnd.api+json; charset=utf-8</contenttype>
<data>
<path>$[*]</path>
<fields>
<id>'id'</id>
<name>'title'</name>
<uri>'url'</uri>
<description>'maintainer'</description>
</fields>
</data>
</urlConfig>
</urls>
<fetchMode>FIRST</fetchMode>
</licenses>
</externalUrls>

View File

@ -0,0 +1,5 @@
import { BaseCriteria } from "../base-criteria";
export class LicenseCriteria extends BaseCriteria {
public type: string;
}

View File

@ -12,6 +12,7 @@ import { ServiceCriteria } from '../../query/service/service-criteria';
import { TagCriteria } from '../../query/tag/tag-criteria';
import { BaseHttpService } from '../http/base-http.service';
import { ConfigurationService } from '../configuration/configuration.service';
import { LicenseCriteria } from '@app/core/query/license/license-criteria';
@Injectable()
export class ExternalSourcesService {
@ -40,6 +41,10 @@ export class ExternalSourcesService {
return this.http.get<ExternalSourceItemModel[]>(this.actionUrl + 'services' + '?query=' + requestItem.criteria.like + '&type=' + requestItem.criteria.type, { headers: this.headers });
}
public searchLicense(requestItem: RequestItem<LicenseCriteria>): Observable<ExternalSourceItemModel[]> {
return this.http.get<ExternalSourceItemModel[]>(this.actionUrl + 'licenses' + '?query=' + requestItem.criteria.like + '&type=' + requestItem.criteria.type, { headers: this.headers });
}
public searchDatasetTags(requestItem: RequestItem<TagCriteria>): Observable<ExternalSourceItemModel[]> {
// return Observable.of([
// { id: '1', name: 'Tag 1', description: '' },

View File

@ -4,9 +4,11 @@ import { BackendErrorValidator } from '@common/forms/validation/custom-validator
export class ExtraPropertiesFormModel {
public language: string;
public license: string;
fromModel(item: any): ExtraPropertiesFormModel {
this.language = item.language;
this.license = item.license;
return this;
}
@ -14,7 +16,8 @@ export class ExtraPropertiesFormModel {
if (context == null) { context = this.createValidationContext(); }
const formGroup = new FormBuilder().group({
language: [{ value: this.language, disabled: disabled }, context.getValidation('language').validators]
language: [{ value: this.language, disabled: disabled }, context.getValidation('language').validators],
license: [{ value: this.license, disabled: disabled }, context.getValidation('license').validators]
});
return formGroup;
}
@ -22,6 +25,7 @@ export class ExtraPropertiesFormModel {
createValidationContext(): ValidationContext {
const baseContext: ValidationContext = new ValidationContext();
baseContext.validation.push({ key: 'language', validators: [] });
baseContext.validation.push({ key: 'license', validators: [] });
return baseContext;
}

View File

@ -81,6 +81,19 @@
</mat-form-field>
<!-- <h4 mat-subheader class="col-12">{{'DMP-EDITOR.FIELDS.PROFILE' | translate}}</h4> -->
</div>
<div class="row pt-3">
<mat-form-field class="col-sm-12 col-md-8">
<!-- <app-multiple-auto-complete [formControl]="formGroup.get('extraProperties').get('language')" placeholder="{{'DMP-EDITOR.FIELDS.RESEARCHERS' | translate}}" [configuration]="researchersAutoCompleteConfiguration">
</app-multiple-auto-complete> -->
<app-single-auto-complete [formControl]="formGroup.get('extraProperties').get('license')" placeholder="{{'DMP-EDITOR.FIELDS.RESEARCHERS' | translate}}" [configuration]="licenseAutoCompleteConfiguration">
</app-single-auto-complete>
<mat-error *ngIf="formGroup.get('extraProperties').get('license').hasError('backendError')">
{{formGroup.get('extraProperties').get('language').getError('backendError').message}}</mat-error>
<mat-error *ngIf="formGroup.get('extraProperties').get('license').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
<!-- <h4 mat-subheader class="col-12">{{'DMP-EDITOR.FIELDS.PROFILE' | translate}}</h4> -->
</div>
<div class="row pt-2">
<mat-form-field class="col-sm-12 col-md-8">
<app-single-auto-complete [required]="false" [formControl]="formGroup.get('profile')" placeholder="{{'DMP-EDITOR.FIELDS.TEMPLATE' | translate}}" [configuration]="dmpProfileAutoCompleteConfiguration">

View File

@ -25,6 +25,7 @@ import { isNullOrUndefined } from 'util';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { LanguageInfoService } from '@app/core/services/culture/language-info-service';
import { LanguageInfo } from '@app/core/model/language-info';
import { LicenseCriteria } from '@app/core/query/license/license-criteria';
@Component({
selector: 'app-general-tab',
@ -67,6 +68,13 @@ export class GeneralTabComponent extends BaseComponent implements OnInit {
titleFn: (item) => item['label']
};
licenseAutoCompleteConfiguration: SingleAutoCompleteConfiguration = {
filterFn: this.licenseSearch.bind(this),
initialItems: (excludedItems: any[]) => this.licenseSearch('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))),
displayFn: (item) => item['name'],
titleFn: (item) => item['name']
};
selectedDmpProfileDefinition: DmpProfileDefinition;
constructor(
@ -120,6 +128,14 @@ export class GeneralTabComponent extends BaseComponent implements OnInit {
return this.dmpProfileService.getPaged(request).pipe(map(x => x.data));
}
licenseSearch(query: string): Observable<ExternalSourceItemModel[]> {
const request = new RequestItem<LicenseCriteria>();
request.criteria = new LicenseCriteria();
request.criteria.like = query;
request.criteria.type = '';
return this.externalSourcesService.searchLicense(request);
}
// onCallbackSuccess(): void {
// this.uiNotificationService.snackBarNotification(this.isNew ? this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-CREATION') : this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success);
// this.router.navigate(['/plans']);
@ -147,7 +163,7 @@ export class GeneralTabComponent extends BaseComponent implements OnInit {
filterProfiles(value: string): Observable<DatasetProfileModel[]> {
const request = new DataTableRequest<DatasetProfileCriteria>(null, null, {fields: ['+label']});
const request = new DataTableRequest<DatasetProfileCriteria>(null, null, { fields: ['+label'] });
const criteria = new DatasetProfileCriteria();
criteria.like = value;
request.criteria = criteria;