Merge remote-tracking branch 'origin/evaluator-plugin' into dmp-refactoring
# Conflicts: # frontend/src/app/ui/description/overview/description-overview.component.ts # frontend/src/app/ui/plan/overview/plan-overview.component.ts
This commit is contained in:
commit
e1bcc362b7
|
@ -95,6 +95,11 @@
|
|||
<artifactId>oidc-authn</artifactId>
|
||||
<version>2.2.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.opencdmp</groupId>
|
||||
<artifactId>evaluator-base</artifactId>
|
||||
<version>0.0.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>gr.cite</groupId>
|
||||
|
|
|
@ -155,6 +155,8 @@ public class AuditableAction {
|
|||
|
||||
public static final EventId FileTransformer_GetAvailableConfigurations = new EventId(20000, "FileTransformer_GetAvailableConfigurations");
|
||||
|
||||
public static final EventId Evaluator_GetAvailableConfigurations = new EventId(20001, "Evaluator_GetAvailableConfigurations");
|
||||
|
||||
public static final EventId ContactSupport_Sent = new EventId(210000, "ContactSupport_Sent");
|
||||
public static final EventId ContactSupport_PublicSent = new EventId(210001, "ContactSupport_PublicSent");
|
||||
|
||||
|
|
|
@ -89,6 +89,7 @@ public final class Permission {
|
|||
public static String AssignPlanUsers = "AssignPlanUsers";
|
||||
public static String InvitePlanUsers = "InvitePlanUsers";
|
||||
public static String AnnotatePlan = "AnnotatePlan";
|
||||
public static String EvaluatePlan = "EvaluatePlan";
|
||||
|
||||
//PlanStatus
|
||||
public static String BrowsePlanStatus = "BrowsePlanStatus";
|
||||
|
@ -137,6 +138,7 @@ public final class Permission {
|
|||
public static String DeleteDescription = "DeleteDescription";
|
||||
public static String CloneDescription = "CloneDescription";
|
||||
public static String ExportDescription = "ExportDescription";
|
||||
public static String EvaluateDescription = "EvaluateDescription";
|
||||
|
||||
//DescriptionTag
|
||||
public static String BrowseDescriptionTag = "BrowseDescriptionTag";
|
||||
|
|
|
@ -11,7 +11,8 @@ public enum TenantConfigurationType implements DatabaseEnum<Short> {
|
|||
FileTransformerPlugins((short) 1),
|
||||
DefaultUserLocale((short) 2),
|
||||
Logo((short) 3),
|
||||
CssColors((short) 4);
|
||||
CssColors((short) 4),
|
||||
EvaluatorPlugins((short) 5);
|
||||
|
||||
private final Short value;
|
||||
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
package org.opencdmp.commons.types.evaluator;
|
||||
|
||||
public class EvaluatorSourceEntity {
|
||||
|
||||
private String url;
|
||||
private String evaluatorId;
|
||||
private String issuerUrl;
|
||||
private String clientId;
|
||||
private String clientSecret;
|
||||
private String scope;
|
||||
private int maxInMemorySizeInBytes;
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getEvaluatorId() {
|
||||
return evaluatorId;
|
||||
}
|
||||
|
||||
public void setEvaluatorId(String evaluatorId) {
|
||||
this.evaluatorId = evaluatorId;
|
||||
}
|
||||
|
||||
public String getIssuerUrl() {
|
||||
return issuerUrl;
|
||||
}
|
||||
|
||||
public void setIssuerUrl(String issuerUrl) {
|
||||
this.issuerUrl = issuerUrl;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getClientSecret() {
|
||||
return clientSecret;
|
||||
}
|
||||
|
||||
public void setClientSecret(String clientSecret) {
|
||||
this.clientSecret = clientSecret;
|
||||
}
|
||||
|
||||
public String getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
public void setScope(String scope) {
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
public int getMaxInMemorySizeInBytes() {
|
||||
return maxInMemorySizeInBytes;
|
||||
}
|
||||
|
||||
public void setMaxInMemorySizeInBytes(int maxInMemorySizeInBytes) {
|
||||
this.maxInMemorySizeInBytes = maxInMemorySizeInBytes;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package org.opencdmp.commons.types.tenantconfiguration;
|
||||
|
||||
import org.opencdmp.commons.types.evaluator.EvaluatorSourceEntity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class EvaluatorTenantConfigurationEntity {
|
||||
|
||||
private List<EvaluatorSourceEntity> sources;
|
||||
private boolean disableSystemSources;
|
||||
|
||||
public List<EvaluatorSourceEntity> getSources() {
|
||||
return sources;
|
||||
}
|
||||
|
||||
public void setSources(List<EvaluatorSourceEntity> sources) {
|
||||
this.sources = sources;
|
||||
}
|
||||
|
||||
public boolean getDisableSystemSources(){
|
||||
return disableSystemSources;
|
||||
}
|
||||
public void setDisableSystemSources(boolean disableSystemSources) {
|
||||
this.disableSystemSources = disableSystemSources;
|
||||
}
|
||||
}
|
|
@ -61,6 +61,7 @@ public class PlanCommonModelBuilder extends BaseCommonModelBuilder<PlanModel, Pl
|
|||
private FileEnvelopeModel pdfFile;
|
||||
private FileEnvelopeModel rdaJsonFile;
|
||||
private String repositoryId;
|
||||
private String evaluatorId;
|
||||
private boolean disableDescriptions;
|
||||
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
|
||||
|
||||
|
@ -96,6 +97,11 @@ public class PlanCommonModelBuilder extends BaseCommonModelBuilder<PlanModel, Pl
|
|||
return this;
|
||||
}
|
||||
|
||||
public PlanCommonModelBuilder setEvaluatorId(String evaluatorId){
|
||||
this.evaluatorId = evaluatorId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PlanCommonModelBuilder setDisableDescriptions(boolean disableDescriptions) {
|
||||
this.disableDescriptions = disableDescriptions;
|
||||
return this;
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package org.opencdmp.model.evaluator;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class EvaluateRequestModel {
|
||||
|
||||
private UUID id;
|
||||
private String evaluatorId;
|
||||
private String format;
|
||||
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(UUID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getEvaluatorId() {
|
||||
return evaluatorId;
|
||||
}
|
||||
|
||||
public void setEvaluatorId(String evaluatorId) {
|
||||
this.evaluatorId = evaluatorId;
|
||||
}
|
||||
|
||||
public String getFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
public void setFormat(String format) {
|
||||
this.format = format;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package org.opencdmp.model.evaluator;
|
||||
|
||||
import org.opencdmp.evaluatorbase.enums.EvaluatorEntityType;
|
||||
import org.opencdmp.evaluatorbase.models.misc.RankConfig;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class EvaluatorConfiguration {
|
||||
private String evaluatorId;
|
||||
private RankConfig rankConfig;
|
||||
private List<EvaluatorEntityType> evaluatorEntityTypes;
|
||||
private boolean useSharedStorage;
|
||||
private boolean hasLogo;
|
||||
|
||||
public String getEvaluatorId() {
|
||||
return evaluatorId;
|
||||
}
|
||||
|
||||
public void setEvaluatorId(String evaluatorId) {
|
||||
this.evaluatorId = evaluatorId;
|
||||
}
|
||||
|
||||
public RankConfig getRankConfig() {
|
||||
return rankConfig;
|
||||
}
|
||||
|
||||
public void setRankConfig(RankConfig rankConfig) {
|
||||
this.rankConfig = rankConfig;
|
||||
}
|
||||
|
||||
public List<EvaluatorEntityType> getEvaluatorEntityTypes() {
|
||||
return evaluatorEntityTypes;
|
||||
}
|
||||
|
||||
public void setEvaluatorEntityTypes(List<EvaluatorEntityType> evaluatorEntityTypes) {
|
||||
this.evaluatorEntityTypes = evaluatorEntityTypes;
|
||||
}
|
||||
|
||||
public boolean isUseSharedStorage() {
|
||||
return useSharedStorage;
|
||||
}
|
||||
|
||||
public void setUseSharedStorage(boolean useSharedStorage) {
|
||||
this.useSharedStorage = useSharedStorage;
|
||||
}
|
||||
|
||||
public boolean isHasLogo() {
|
||||
return hasLogo;
|
||||
}
|
||||
|
||||
public void setHasLogo(boolean hasLogo) {
|
||||
this.hasLogo = hasLogo;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package org.opencdmp.model.evaluator;
|
||||
|
||||
public class EvaluatorSource {
|
||||
|
||||
private String url;
|
||||
|
||||
public static final String _url = "url";
|
||||
|
||||
private String evaluatorId;
|
||||
|
||||
public static final String _evaluatorId = "evaluatorId";
|
||||
|
||||
private String issuerUrl;
|
||||
|
||||
public static final String _issuerUrl = "issuerUrl";
|
||||
|
||||
private String clientId;
|
||||
|
||||
public static final String _clientId = "clientId";
|
||||
|
||||
private String clientSecret;
|
||||
|
||||
public static final String _clientSecret = "clientSecret";
|
||||
|
||||
private String scope;
|
||||
|
||||
public static final String _scope = "scope";
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getEvaluatorId() {
|
||||
return evaluatorId;
|
||||
}
|
||||
|
||||
public void setEvaluatorId(String evaluatorId) {
|
||||
this.evaluatorId = evaluatorId;
|
||||
}
|
||||
|
||||
public String getIssuerUrl() {
|
||||
return issuerUrl;
|
||||
}
|
||||
|
||||
public void setIssuerUrl(String issuerUrl) {
|
||||
this.issuerUrl = issuerUrl;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getClientSecret() {
|
||||
return clientSecret;
|
||||
}
|
||||
|
||||
public void setClientSecret(String clientSecret) {
|
||||
this.clientSecret = clientSecret;
|
||||
}
|
||||
|
||||
public String getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
public void setScope(String scope) {
|
||||
this.scope = scope;
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ package org.opencdmp.model.tenantconfiguration;
|
|||
|
||||
import org.opencdmp.commons.enums.IsActive;
|
||||
import org.opencdmp.commons.enums.TenantConfigurationType;
|
||||
import org.opencdmp.commons.types.tenantconfiguration.EvaluatorTenantConfigurationEntity;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.UUID;
|
||||
|
@ -36,6 +37,10 @@ public class TenantConfiguration {
|
|||
|
||||
public static final String _fileTransformerPlugins = "fileTransformerPlugins";
|
||||
|
||||
private EvaluatorTenantConfigurationEntity evaluatorPlugins;
|
||||
|
||||
public static final String _evaluatorPlugins = "evaluatorPlugins";
|
||||
|
||||
private LogoTenantConfiguration logo;
|
||||
|
||||
public static final String _logo = "logo";
|
||||
|
@ -150,4 +155,12 @@ public class TenantConfiguration {
|
|||
public void setType(TenantConfigurationType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public EvaluatorTenantConfigurationEntity getEvaluatorPlugins() {
|
||||
return evaluatorPlugins;
|
||||
}
|
||||
|
||||
public void setEvaluatorPlugins(EvaluatorTenantConfigurationEntity evaluatorPlugins) {
|
||||
this.evaluatorPlugins = evaluatorPlugins;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
package org.opencdmp.service.evaluator;
|
||||
|
||||
import com.sun.jdi.InvalidTypeException;
|
||||
import gr.cite.tools.logging.LoggerService;
|
||||
import gr.cite.tools.logging.MapLogEntry;
|
||||
import org.opencdmp.commonmodels.models.description.DescriptionModel;
|
||||
import org.opencdmp.commonmodels.models.plan.PlanModel;
|
||||
import org.opencdmp.evaluatorbase.interfaces.EvaluatorClient;
|
||||
import org.opencdmp.evaluatorbase.interfaces.EvaluatorConfiguration;
|
||||
import org.opencdmp.evaluatorbase.models.misc.RankModel;
|
||||
import org.opencdmp.service.filetransformer.FileTransformerRepository;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.management.InvalidApplicationException;
|
||||
import java.io.IOException;
|
||||
|
||||
public class EvaluatorClientImpl implements EvaluatorClient {
|
||||
|
||||
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(FileTransformerRepository.class));
|
||||
|
||||
private final WebClient transformerClient;
|
||||
|
||||
public EvaluatorClientImpl(WebClient transformerClient) {
|
||||
this.transformerClient = transformerClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RankModel rankPlan(PlanModel planModel) throws InvalidApplicationException, IOException, InvalidTypeException {
|
||||
logger.debug(new MapLogEntry("rankPlan").And("planModel", planModel));
|
||||
|
||||
return this.transformerClient.post().uri("/rank/plan").bodyValue(planModel) // Send planModel in the body
|
||||
.exchangeToMono(mono -> mono.statusCode().isError() ? mono.createException().flatMap(Mono::error) : mono.bodyToMono(RankModel.class)).block();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RankModel rankDescription(DescriptionModel descriptionModel) throws InvalidApplicationException, IOException {
|
||||
logger.debug(new MapLogEntry("rankDescription").And("descriptionModel", descriptionModel));
|
||||
return this.transformerClient.post().uri("/rank/description").bodyValue(descriptionModel) // Send descriptionModel in the body
|
||||
.exchangeToMono(mono -> mono.statusCode().isError() ? mono.createException().flatMap(Mono::error) : mono.bodyToMono(RankModel.class)).block();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public EvaluatorConfiguration getConfiguration() {
|
||||
logger.debug(new MapLogEntry("getConfiguration"));
|
||||
return this.transformerClient.get().uri("/config")
|
||||
.exchangeToMono(mono -> mono.statusCode().isError() ? mono.createException().flatMap(Mono::error) : mono.bodyToMono(new ParameterizedTypeReference<EvaluatorConfiguration>() {})).block();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLogo() {
|
||||
logger.debug(new MapLogEntry("getLogo"));
|
||||
return this.transformerClient.get().uri("/logo").exchangeToMono(mono -> mono.statusCode().isError() ? mono.createException().flatMap(Mono::error) : mono.bodyToMono(String.class)).block();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package org.opencdmp.service.evaluator;
|
||||
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties({EvaluatorProperties.class})
|
||||
public class EvaluatorConfiguration {
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package org.opencdmp.service.evaluator;
|
||||
|
||||
import gr.cite.tools.cache.CacheOptions;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "cache.evaluator-config-by-id")
|
||||
public class EvaluatorConfigurationCacheOptions extends CacheOptions {
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package org.opencdmp.service.evaluator;
|
||||
|
||||
import gr.cite.tools.cache.CacheService;
|
||||
import org.opencdmp.evaluatorbase.interfaces.EvaluatorConfiguration;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
@Service
|
||||
public class EvaluatorConfigurationCacheService extends CacheService<EvaluatorConfigurationCacheService.EvaluatorConfigurationCacheValue> {
|
||||
|
||||
public static class EvaluatorConfigurationCacheValue {
|
||||
|
||||
public EvaluatorConfigurationCacheValue() {
|
||||
}
|
||||
|
||||
public EvaluatorConfigurationCacheValue(String repositoryId, String tenantCode, EvaluatorConfiguration configuration) {
|
||||
this.evaluatorId = repositoryId;
|
||||
this.configuration = configuration;
|
||||
this.tenantCode = tenantCode == null ? "" : tenantCode;
|
||||
}
|
||||
|
||||
private String evaluatorId;
|
||||
private String tenantCode;
|
||||
|
||||
public String getEvaluatorId() {
|
||||
return evaluatorId;
|
||||
}
|
||||
|
||||
public void setEvaluatorId(String evaluatorId) {
|
||||
this.evaluatorId = evaluatorId;
|
||||
}
|
||||
|
||||
private EvaluatorConfiguration configuration;
|
||||
|
||||
public EvaluatorConfiguration getConfiguration() {
|
||||
return configuration;
|
||||
}
|
||||
|
||||
public void setConfiguration(EvaluatorConfiguration configuration) {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
public String getTenantCode() {
|
||||
return tenantCode;
|
||||
}
|
||||
|
||||
public void setTenantCode(String tenantCode) {
|
||||
this.tenantCode = tenantCode;
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public EvaluatorConfigurationCacheService(EvaluatorConfigurationCacheOptions options) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<EvaluatorConfigurationCacheService.EvaluatorConfigurationCacheValue> valueClass() {
|
||||
return EvaluatorConfigurationCacheService.EvaluatorConfigurationCacheValue.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String keyOf(EvaluatorConfigurationCacheService.EvaluatorConfigurationCacheValue value) {
|
||||
return this.buildKey(value.getEvaluatorId(), value.getTenantCode());
|
||||
}
|
||||
|
||||
|
||||
public String buildKey(String evaluatorId, String tenantCod) {
|
||||
HashMap<String, String> keyParts = new HashMap<>();
|
||||
keyParts.put("$evaluatorId$", evaluatorId);
|
||||
keyParts.put("$tenantCode$", tenantCod);
|
||||
return this.generateKey(keyParts);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package org.opencdmp.service.evaluator;
|
||||
|
||||
import org.opencdmp.commons.types.evaluator.EvaluatorSourceEntity;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ConfigurationProperties(prefix = "evaluator")
|
||||
public class EvaluatorProperties {
|
||||
|
||||
private List<EvaluatorSourceEntity> sources;
|
||||
|
||||
public List<EvaluatorSourceEntity> getSources() {
|
||||
return sources;
|
||||
}
|
||||
|
||||
public void setSources(List<EvaluatorSourceEntity> sources) {
|
||||
this.sources = sources;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package org.opencdmp.service.evaluator;
|
||||
|
||||
import com.sun.jdi.InvalidTypeException;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.management.InvalidApplicationException;
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface EvaluatorService {
|
||||
|
||||
org.opencdmp.evaluatorbase.models.misc.RankModel rankPlan(UUID planId, String repositoryId, String format, boolean isPublic) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, IOException, InvalidTypeException;
|
||||
|
||||
org.opencdmp.evaluatorbase.models.misc.RankModel rankDescription(UUID descriptionId, String repositoryId, String format, boolean isPublic) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, IOException;
|
||||
|
||||
List<org.opencdmp.evaluatorbase.interfaces.EvaluatorConfiguration> getAvailableEvaluators() throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException;
|
||||
|
||||
String getLogo(String evaluatorId) throws InvalidApplicationException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException;
|
||||
|
||||
}
|
|
@ -0,0 +1,326 @@
|
|||
package org.opencdmp.service.evaluator;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import com.sun.jdi.InvalidTypeException;
|
||||
import gr.cite.commons.web.authz.service.AuthorizationService;
|
||||
import gr.cite.commons.web.oidc.filter.webflux.TokenExchangeCacheService;
|
||||
import gr.cite.commons.web.oidc.filter.webflux.TokenExchangeFilterFunction;
|
||||
import gr.cite.commons.web.oidc.filter.webflux.TokenExchangeModel;
|
||||
import gr.cite.tools.data.builder.BuilderFactory;
|
||||
import gr.cite.tools.data.query.QueryFactory;
|
||||
import gr.cite.tools.exception.MyNotFoundException;
|
||||
import gr.cite.tools.fieldset.BaseFieldSet;
|
||||
import gr.cite.tools.logging.LoggerService;
|
||||
import gr.cite.tools.logging.MapLogEntry;
|
||||
import org.opencdmp.authorization.AuthorizationFlags;
|
||||
import org.opencdmp.authorization.Permission;
|
||||
import org.opencdmp.commonmodels.models.description.DescriptionModel;
|
||||
import org.opencdmp.commons.JsonHandlingService;
|
||||
import org.opencdmp.commons.enums.*;
|
||||
import org.opencdmp.commons.scope.tenant.TenantScope;
|
||||
import org.opencdmp.commons.types.evaluator.EvaluatorSourceEntity;
|
||||
import org.opencdmp.depositbase.repository.DepositClient;
|
||||
import org.opencdmp.evaluatorbase.interfaces.EvaluatorClient;
|
||||
import org.opencdmp.evaluatorbase.interfaces.EvaluatorConfiguration;
|
||||
import org.opencdmp.commons.types.tenantconfiguration.EvaluatorTenantConfigurationEntity;
|
||||
import org.opencdmp.convention.ConventionService;
|
||||
import org.opencdmp.data.DescriptionEntity;
|
||||
import org.opencdmp.data.PlanEntity;
|
||||
import org.opencdmp.data.TenantConfigurationEntity;
|
||||
import org.opencdmp.data.TenantEntityManager;
|
||||
import org.opencdmp.evaluatorbase.models.misc.RankModel;
|
||||
import org.opencdmp.event.TenantConfigurationTouchedEvent;
|
||||
import org.opencdmp.model.builder.commonmodels.DepositConfigurationBuilder;
|
||||
import org.opencdmp.model.builder.commonmodels.description.DescriptionCommonModelBuilder;
|
||||
import org.opencdmp.model.builder.commonmodels.plan.PlanCommonModelBuilder;
|
||||
import org.opencdmp.model.description.Description;
|
||||
import org.opencdmp.model.plan.Plan;
|
||||
import org.opencdmp.commonmodels.models.plan.PlanModel;
|
||||
import org.opencdmp.model.tenantconfiguration.TenantConfiguration;
|
||||
import org.opencdmp.query.DescriptionQuery;
|
||||
import org.opencdmp.query.PlanQuery;
|
||||
import org.opencdmp.query.TenantConfigurationQuery;
|
||||
import org.opencdmp.service.accounting.AccountingService;
|
||||
import org.opencdmp.service.encryption.EncryptionService;
|
||||
import org.opencdmp.service.tenant.TenantProperties;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.json.Jackson2JsonDecoder;
|
||||
import org.springframework.http.codec.json.Jackson2JsonEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.management.InvalidApplicationException;
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.*;
|
||||
|
||||
import static org.opencdmp.authorization.AuthorizationFlags.Public;
|
||||
|
||||
@Service
|
||||
public class EvaluatorServiceImpl implements EvaluatorService {
|
||||
|
||||
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(EvaluatorServiceImpl.class));
|
||||
private final EvaluatorProperties evaluatorProperties;
|
||||
private final Map<String, EvaluatorClientImpl> clients;
|
||||
private final TokenExchangeCacheService tokenExchangeCacheService;
|
||||
private final EvaluatorConfigurationCacheService evaluatorConfigurationCacheService;
|
||||
private final AuthorizationService authorizationService;
|
||||
private final QueryFactory queryFactory;
|
||||
private final BuilderFactory builderFactory;
|
||||
private final MessageSource messageSource;
|
||||
private final ConventionService conventionService;
|
||||
private final TenantScope tenantScope;
|
||||
private final EncryptionService encryptionService;
|
||||
private final TenantProperties tenantProperties;
|
||||
private final JsonHandlingService jsonHandlingService;
|
||||
private final EvaluatorSourcesCacheService evaluatorSourcesCacheService;
|
||||
private final AccountingService accountingService;
|
||||
private final TenantEntityManager entityManager;
|
||||
|
||||
@Autowired
|
||||
public EvaluatorServiceImpl(EvaluatorProperties evaluatorProperties, Map<String, EvaluatorClientImpl> clients, TokenExchangeCacheService tokenExchangeCacheService, EvaluatorConfigurationCacheService evaluatorConfigurationCacheService, AuthorizationService authorizationService, QueryFactory queryFactory, BuilderFactory builderFactory, MessageSource messageSource, ConventionService conventionService, TenantScope tenantScope, EncryptionService encryptionService, TenantProperties tenantProperties, JsonHandlingService jsonHandlingService, EvaluatorSourcesCacheService evaluatorSourcesCacheService, AccountingService accountingService, TenantEntityManager entityManager) {
|
||||
this.evaluatorProperties = evaluatorProperties;
|
||||
this.clients = clients;
|
||||
this.tokenExchangeCacheService = tokenExchangeCacheService;
|
||||
this.evaluatorConfigurationCacheService = evaluatorConfigurationCacheService;
|
||||
this.authorizationService = authorizationService;
|
||||
this.queryFactory = queryFactory;
|
||||
this.builderFactory = builderFactory;
|
||||
this.messageSource = messageSource;
|
||||
this.conventionService = conventionService;
|
||||
this.tenantScope = tenantScope;
|
||||
this.encryptionService = encryptionService;
|
||||
this.tenantProperties = tenantProperties;
|
||||
this.jsonHandlingService = jsonHandlingService;
|
||||
this.evaluatorSourcesCacheService = evaluatorSourcesCacheService;
|
||||
this.accountingService = accountingService;
|
||||
this.entityManager = entityManager;
|
||||
}
|
||||
private EvaluatorClientImpl getEvaluatorClient(String repoId) throws InvalidApplicationException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
|
||||
String repositoryIdByTenant = this.getRepositoryIdByTenant(repoId);
|
||||
if (this.clients.containsKey(repositoryIdByTenant)) {
|
||||
return this.clients.get(repositoryIdByTenant);
|
||||
}
|
||||
|
||||
EvaluatorSourceEntity source = this.getEvaluatorSources().stream()
|
||||
.filter(evaluatorSourceEntity -> evaluatorSourceEntity.getEvaluatorId().equals(repoId))
|
||||
.findFirst().orElse(null);
|
||||
|
||||
try {
|
||||
TokenExchangeModel tokenExchangeModel = new TokenExchangeModel(
|
||||
"evaluator:" + repositoryIdByTenant,
|
||||
source.getIssuerUrl(),
|
||||
source.getClientId(),
|
||||
source.getClientSecret(),
|
||||
source.getScope()
|
||||
);
|
||||
|
||||
TokenExchangeFilterFunction tokenExchangeFilterFunction = new TokenExchangeFilterFunction(
|
||||
this.tokenExchangeCacheService,
|
||||
tokenExchangeModel
|
||||
);
|
||||
|
||||
EvaluatorClientImpl repository = new EvaluatorClientImpl(
|
||||
WebClient.builder().baseUrl(source.getUrl() + "/api/evaluator")
|
||||
.filters(exchangeFilterFunctions -> {
|
||||
exchangeFilterFunctions.add(tokenExchangeFilterFunction);
|
||||
exchangeFilterFunctions.add(logRequest());
|
||||
exchangeFilterFunctions.add(logResponse());
|
||||
})
|
||||
.codecs(codecs -> {
|
||||
codecs.defaultCodecs().maxInMemorySize(source.getMaxInMemorySizeInBytes());
|
||||
codecs.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(new ObjectMapper().registerModule(new JavaTimeModule()), MediaType.APPLICATION_JSON));
|
||||
codecs.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(new ObjectMapper().registerModule(new JavaTimeModule()), MediaType.APPLICATION_JSON));
|
||||
})
|
||||
.build()
|
||||
);
|
||||
|
||||
this.clients.put(repositoryIdByTenant, repository);
|
||||
return repository;
|
||||
} catch (Exception e) {
|
||||
logger.error("Exception occurred while creating EvaluatorClientImpl", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private List<EvaluatorSourceEntity> getEvaluatorSources() throws InvalidApplicationException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
|
||||
String tenantCode = this.tenantScope.isSet() && this.tenantScope.isMultitenant() ? this.tenantScope.getTenantCode() : "";
|
||||
EvaluatorSourcesCacheService.EvaluatorSourceCacheValue cacheValue = this.evaluatorSourcesCacheService.lookup(this.evaluatorSourcesCacheService.buildKey(tenantCode));
|
||||
if (cacheValue == null) {
|
||||
List<EvaluatorSourceEntity> evaluatorSourceEntities = new ArrayList<>(this.evaluatorProperties.getSources());
|
||||
|
||||
if (this.tenantScope.isSet() && this.tenantScope.isMultitenant()) {
|
||||
TenantConfigurationQuery tenantConfigurationQuery = this.queryFactory.query(TenantConfigurationQuery.class).disableTracking().isActive(IsActive.Active).types(TenantConfigurationType.EvaluatorPlugins);
|
||||
if (this.tenantScope.isDefaultTenant()) tenantConfigurationQuery.tenantIsSet(false);
|
||||
else tenantConfigurationQuery.tenantIsSet(true).tenantIds(this.tenantScope.getTenant());
|
||||
TenantConfigurationEntity tenantConfiguration = tenantConfigurationQuery.firstAs(new BaseFieldSet().ensure(TenantConfiguration._evaluatorPlugins));
|
||||
|
||||
if (tenantConfiguration != null && !this.conventionService.isNullOrEmpty(tenantConfiguration.getValue())) {
|
||||
EvaluatorTenantConfigurationEntity evaluatorTenantConfigurationEntity = this.jsonHandlingService.fromJsonSafe(EvaluatorTenantConfigurationEntity.class, tenantConfiguration.getValue());
|
||||
if (evaluatorTenantConfigurationEntity != null) {
|
||||
if (evaluatorTenantConfigurationEntity.getDisableSystemSources()) evaluatorSourceEntities = new ArrayList<>();
|
||||
evaluatorSourceEntities.addAll(this.buildEvaluatorSourceItems(evaluatorTenantConfigurationEntity.getSources()));
|
||||
}
|
||||
}
|
||||
}
|
||||
cacheValue = new EvaluatorSourcesCacheService.EvaluatorSourceCacheValue(tenantCode, evaluatorSourceEntities);
|
||||
this.evaluatorSourcesCacheService.put(cacheValue);
|
||||
}
|
||||
return cacheValue.getSources();
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void handleTenantConfigurationTouchedEvent(TenantConfigurationTouchedEvent event) {
|
||||
if (!event.getType().equals(TenantConfigurationType.FileTransformerPlugins)) return;
|
||||
EvaluatorSourcesCacheService.EvaluatorSourceCacheValue evaluatorSourceCacheValue = this.evaluatorSourcesCacheService.lookup(this.evaluatorSourcesCacheService.buildKey(event.getTenantCode()));
|
||||
if (evaluatorSourceCacheValue != null && evaluatorSourceCacheValue.getSources() != null){
|
||||
for (EvaluatorSourceEntity source : evaluatorSourceCacheValue.getSources()){
|
||||
String repositoryIdByTenant = source.getEvaluatorId() + "_" + event.getTenantCode();
|
||||
this.clients.remove(repositoryIdByTenant);
|
||||
this.evaluatorConfigurationCacheService.evict(this.evaluatorConfigurationCacheService.buildKey(source.getEvaluatorId(), event.getTenantCode()));
|
||||
}
|
||||
}
|
||||
this.evaluatorConfigurationCacheService.evict(this.evaluatorSourcesCacheService.buildKey(event.getTenantCode()));
|
||||
}
|
||||
|
||||
private List<EvaluatorSourceEntity> buildEvaluatorSourceItems(List<EvaluatorSourceEntity> sources) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
|
||||
List<EvaluatorSourceEntity> items = new ArrayList<>();
|
||||
if (this.conventionService.isListNullOrEmpty(sources)) return items;
|
||||
for (EvaluatorSourceEntity source : sources){
|
||||
EvaluatorSourceEntity item = new EvaluatorSourceEntity();
|
||||
item.setEvaluatorId(source.getEvaluatorId());
|
||||
item.setUrl(source.getUrl());
|
||||
item.setIssuerUrl(source.getIssuerUrl());
|
||||
item.setClientId(source.getClientId());
|
||||
if (!this.conventionService.isNullOrEmpty(source.getClientSecret())) item.setClientSecret(this.encryptionService.decryptAES(source.getClientSecret(), this.tenantProperties.getConfigEncryptionAesKey(), this.tenantProperties.getConfigEncryptionAesIv()));
|
||||
item.setScope(source.getScope());
|
||||
items.add(item);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
private String getRepositoryIdByTenant(String repositoryId) throws InvalidApplicationException {
|
||||
if (this.tenantScope.isSet() && this.tenantScope.isMultitenant()) {
|
||||
return repositoryId + "_" + this.tenantScope.getTenantCode();
|
||||
} else {
|
||||
return repositoryId;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<org.opencdmp.evaluatorbase.interfaces.EvaluatorConfiguration> getAvailableEvaluators() throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
|
||||
|
||||
this.authorizationService.authorizeForce(Permission.BrowsePlan, Permission.DeferredAffiliation);
|
||||
List<org.opencdmp.evaluatorbase.interfaces.EvaluatorConfiguration> configurations = new ArrayList<>();
|
||||
|
||||
for(EvaluatorSourceEntity evaluatorSource : this.getEvaluatorSources()){
|
||||
|
||||
String tenantCode = this.tenantScope.isSet() && this.tenantScope.isMultitenant() ? this.tenantScope.getTenantCode() : "";
|
||||
EvaluatorConfigurationCacheService.EvaluatorConfigurationCacheValue cacheValue = this.evaluatorConfigurationCacheService.lookup(this.evaluatorConfigurationCacheService.buildKey(evaluatorSource.getEvaluatorId(), tenantCode));
|
||||
|
||||
if(cacheValue == null){
|
||||
try{
|
||||
EvaluatorClientImpl evaluatorClient = this.getEvaluatorClient(evaluatorSource.getEvaluatorId());
|
||||
if(evaluatorClient == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{evaluatorSource.getEvaluatorId(), EvaluatorClientImpl.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
|
||||
EvaluatorConfiguration configuration = evaluatorClient.getConfiguration();
|
||||
cacheValue = new EvaluatorConfigurationCacheService.EvaluatorConfigurationCacheValue(evaluatorSource.getEvaluatorId(), tenantCode, configuration);
|
||||
this.evaluatorConfigurationCacheService.put(cacheValue);
|
||||
}catch (Exception e){
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
if(cacheValue != null){
|
||||
configurations.add(cacheValue.getConfiguration());
|
||||
}
|
||||
}
|
||||
|
||||
return configurations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RankModel rankPlan(UUID planId, String evaluatorId, String format, boolean isPublic) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, IOException, InvalidTypeException {
|
||||
this.authorizationService.authorizeForce(Permission.EvaluatePlan);
|
||||
EvaluatorClientImpl repository = this.getEvaluatorClient(evaluatorId);
|
||||
|
||||
if(repository == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{format, EvaluatorClientImpl.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
|
||||
PlanEntity planEntity = this.queryFactory.query(PlanQuery.class).disableTracking().ids(planId).first();
|
||||
if (planEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{planId, PlanEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
|
||||
PlanModel evaluatorModel = this.builderFactory.builder(PlanCommonModelBuilder.class).useSharedStorage(repository.getConfiguration().isUseSharedStorage()).setEvaluatorId(repository.getConfiguration().getEvaluatorId()).isPublic(isPublic).authorize(AuthorizationFlags.All).build(planEntity);
|
||||
if(evaluatorModel == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{planId, Plan.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
|
||||
this.accountingService.increase(UsageLimitTargetMetric.FILE_TRANSFORMER_EXPORT_PLAN_EXECUTION_COUNT.getValue());
|
||||
this.increaseTargetMetricWithRepositoryId(UsageLimitTargetMetric.FILE_TRANSFORMER_EXPORT_PLAN_EXECUTION_COUNT_FOR, evaluatorId);
|
||||
|
||||
return repository.rankPlan(evaluatorModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RankModel rankDescription(UUID descriptionId, String repositoryId, String format, boolean isPublic) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, IOException {
|
||||
this.authorizationService.authorizeForce(Permission.EvaluateDescription);
|
||||
EvaluatorClientImpl repository = this.getEvaluatorClient(repositoryId);
|
||||
|
||||
if(repository == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{format, EvaluatorClientImpl.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
|
||||
DescriptionEntity descriptionEntity = this.queryFactory.query(DescriptionQuery.class).disableTracking().ids(descriptionId).first();
|
||||
if (descriptionEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{descriptionId, DescriptionEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
|
||||
DescriptionModel descriptionEvaluatorModel = this.builderFactory.builder(DescriptionCommonModelBuilder.class).setRepositoryId(repository.getConfiguration().getEvaluatorId()).useSharedStorage(repository.getConfiguration().isUseSharedStorage()).isPublic(isPublic).authorize(AuthorizationFlags.All).build(descriptionEntity);
|
||||
if (descriptionEvaluatorModel == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{descriptionId, Description.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
|
||||
this.accountingService.increase(UsageLimitTargetMetric.FILE_TRANSFORMER_EXPORT_DESCRIPTIONS_EXECUTION_COUNT.getValue());
|
||||
this.increaseTargetMetricWithRepositoryId(UsageLimitTargetMetric.FILE_TRANSFORMER_EXPORT_DESCRIPTIONS_EXECUTION_COUNT_FOR, repositoryId);
|
||||
|
||||
return repository.rankDescription(descriptionEvaluatorModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLogo(String evaluatorId) throws InvalidApplicationException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
|
||||
|
||||
this.authorizationService.authorizeForce(Permission.BrowseDeposit, Permission.DeferredAffiliation);
|
||||
EvaluatorClient evaluatorClient = this.getEvaluatorClient(evaluatorId);
|
||||
if(evaluatorClient == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{evaluatorId, EvaluatorClient.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
return evaluatorClient.getLogo();
|
||||
}
|
||||
|
||||
private static ExchangeFilterFunction logRequest() {
|
||||
return ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
|
||||
logger.debug(new MapLogEntry("Request").And("method", clientRequest.method().toString()).And("url", clientRequest.url()));
|
||||
return Mono.just(clientRequest);
|
||||
});
|
||||
}
|
||||
|
||||
private static ExchangeFilterFunction logResponse() {
|
||||
return ExchangeFilterFunction.ofResponseProcessor(response -> {
|
||||
if (response.statusCode().isError()) {
|
||||
return response.mutate().build().bodyToMono(String.class)
|
||||
.flatMap(body -> {
|
||||
logger.error(new MapLogEntry("Response").And("method", response.request().getMethod().toString()).And("url", response.request().getURI()).And("status", response.statusCode().toString()).And("body", body));
|
||||
return Mono.just(response);
|
||||
});
|
||||
}
|
||||
return Mono.just(response);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private void increaseTargetMetricWithRepositoryId(UsageLimitTargetMetric metric, String repositoryId) throws InvalidApplicationException {
|
||||
this.accountingService.increase(metric.getValue() + repositoryId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package org.opencdmp.service.evaluator;
|
||||
|
||||
import gr.cite.tools.cache.CacheOptions;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "cache.evaluator-sources-by-tenant")
|
||||
public class EvaluatorSourcesCacheOptions extends CacheOptions {
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package org.opencdmp.service.evaluator;
|
||||
|
||||
import gr.cite.tools.cache.CacheService;
|
||||
import org.opencdmp.commons.types.evaluator.EvaluatorSourceEntity;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class EvaluatorSourcesCacheService extends CacheService<EvaluatorSourcesCacheService.EvaluatorSourceCacheValue> {
|
||||
|
||||
public static class EvaluatorSourceCacheValue {
|
||||
|
||||
public EvaluatorSourceCacheValue() {
|
||||
}
|
||||
|
||||
public EvaluatorSourceCacheValue(String tenantCode, List<EvaluatorSourceEntity> sources) {
|
||||
this.tenantCode = tenantCode;
|
||||
this.sources = sources;
|
||||
}
|
||||
|
||||
private String tenantCode;
|
||||
|
||||
private List<EvaluatorSourceEntity> sources;
|
||||
|
||||
|
||||
public String getTenantCode() {
|
||||
return tenantCode;
|
||||
}
|
||||
|
||||
public void setTenantCode(String tenantCode) {
|
||||
this.tenantCode = tenantCode;
|
||||
}
|
||||
|
||||
public List<EvaluatorSourceEntity> getSources() {
|
||||
return sources;
|
||||
}
|
||||
|
||||
public void setSources(List<EvaluatorSourceEntity> sources) {
|
||||
this.sources = sources;
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public EvaluatorSourcesCacheService(EvaluatorSourcesCacheOptions options) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<EvaluatorSourceCacheValue> valueClass() {
|
||||
return EvaluatorSourceCacheValue.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String keyOf(EvaluatorSourceCacheValue value) {
|
||||
return this.buildKey(value.getTenantCode());
|
||||
}
|
||||
|
||||
public String buildKey(String tenantCod) {
|
||||
HashMap<String, String> keyParts = new HashMap<>();
|
||||
keyParts.put("$tenantCode$", tenantCod);
|
||||
return this.generateKey(keyParts);
|
||||
}
|
||||
}
|
|
@ -151,8 +151,6 @@ public class FileTransformerServiceImpl implements FileTransformerService {
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private List<FileTransformerSourceEntity> getFileTransformerSources() throws InvalidApplicationException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
|
||||
String tenantCode = this.tenantScope.isSet() && this.tenantScope.isMultitenant() ? this.tenantScope.getTenantCode() : "";
|
||||
FileTransformerSourcesCacheService.FileTransformerSourceCacheValue cacheValue = this.fileTransformerSourcesCacheService.lookup(this.fileTransformerSourcesCacheService.buildKey(tenantCode));
|
||||
|
|
|
@ -869,8 +869,6 @@ public class PlanServiceImpl implements PlanService {
|
|||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void updateVersionStatusAndSave(PlanEntity data, PlanStatus previousStatus, PlanStatus newStatus) throws InvalidApplicationException {
|
||||
if (previousStatus == null && newStatus == null)
|
||||
return;
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
package org.opencdmp.controllers;
|
||||
|
||||
import gr.cite.tools.auditing.AuditService;
|
||||
import gr.cite.tools.logging.LoggerService;
|
||||
import gr.cite.tools.logging.MapLogEntry;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.ArraySchema;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import org.opencdmp.audit.AuditableAction;
|
||||
import org.opencdmp.controllers.swagger.SwaggerHelpers;
|
||||
import org.opencdmp.controllers.swagger.annotation.OperationWithTenantHeader;
|
||||
import org.opencdmp.controllers.swagger.annotation.Swagger404;
|
||||
import org.opencdmp.controllers.swagger.annotation.SwaggerCommonErrorResponses;
|
||||
import org.opencdmp.evaluatorbase.interfaces.EvaluatorConfiguration;
|
||||
import org.opencdmp.evaluatorbase.models.misc.RankModel;
|
||||
import org.opencdmp.model.evaluator.EvaluateRequestModel;
|
||||
import org.opencdmp.model.file.ExportRequestModel;
|
||||
import org.opencdmp.service.evaluator.EvaluatorService;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.management.InvalidApplicationException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@CrossOrigin
|
||||
@RequestMapping(value = "/api/evaluator")
|
||||
@SwaggerCommonErrorResponses
|
||||
public class EvaluatorController {
|
||||
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(EvaluatorController.class));
|
||||
|
||||
private final EvaluatorService evaluatorService;
|
||||
|
||||
private final AuditService auditService;
|
||||
|
||||
@Autowired
|
||||
public EvaluatorController(EvaluatorService evaluatorService, AuditService auditService){
|
||||
this.evaluatorService = evaluatorService;
|
||||
this.auditService = auditService;
|
||||
}
|
||||
|
||||
@GetMapping("/available")
|
||||
@OperationWithTenantHeader(summary = "Fetch all evaluators", description = SwaggerHelpers.Evaluator.endpoint_get_available_evaluators,
|
||||
responses = @ApiResponse(description = "OK", responseCode = "200", content = @Content(
|
||||
array = @ArraySchema(
|
||||
schema = @Schema(
|
||||
implementation = EvaluatorConfiguration.class
|
||||
)))
|
||||
))
|
||||
public List<EvaluatorConfiguration> getAvailableConfigurations() throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
|
||||
logger.debug(new MapLogEntry("getAvailableConfigurations"));
|
||||
|
||||
List<EvaluatorConfiguration> model = this.evaluatorService.getAvailableEvaluators();
|
||||
this.auditService.track(AuditableAction.Evaluator_GetAvailableConfigurations);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
@PostMapping("/rank-plan")
|
||||
@OperationWithTenantHeader(summary = "Rank a plan", description = SwaggerHelpers.Evaluator.endpoint_rank_plans,
|
||||
responses = @ApiResponse(description = "OK", responseCode = "200"))
|
||||
public ResponseEntity<RankModel> rankPlan(@RequestBody EvaluateRequestModel requestModel) throws Exception {
|
||||
logger.debug(new MapLogEntry("ranking plan"));
|
||||
|
||||
RankModel rankModel = this.evaluatorService.rankPlan(requestModel.getId(), requestModel.getEvaluatorId(), requestModel.getFormat(), true);
|
||||
|
||||
return new ResponseEntity<>(rankModel, HttpStatus.OK);
|
||||
}
|
||||
|
||||
@PostMapping("/rank-description")
|
||||
@OperationWithTenantHeader(summary = "Rank a description", description = SwaggerHelpers.Evaluator.endpoint_rank_descriptions,
|
||||
responses = @ApiResponse(description = "OK", responseCode = "200"))
|
||||
public ResponseEntity<RankModel> rankDescription(@RequestBody EvaluateRequestModel requestModel) throws Exception {
|
||||
logger.debug(new MapLogEntry("ranking description"));
|
||||
|
||||
RankModel rankModel = this.evaluatorService.rankDescription(requestModel.getId(), requestModel.getEvaluatorId(), requestModel.getFormat(), true);
|
||||
|
||||
return new ResponseEntity<>(rankModel, HttpStatus.OK);
|
||||
}
|
||||
|
||||
@GetMapping("/{evaluatorId}/logo")
|
||||
@OperationWithTenantHeader(summary = "Fetch a specific evaluator logo by id", description = SwaggerHelpers.Deposit.endpoint_get_logo,
|
||||
responses = @ApiResponse(description = "OK", responseCode = "200", content = @Content(
|
||||
schema = @Schema(
|
||||
implementation = String.class
|
||||
))
|
||||
))
|
||||
@Swagger404
|
||||
public String getLogo(
|
||||
@Parameter(name = "evaluatorId", description = "The id of an evaluator of which to fetch the logo", example = "zenodo", required = true) @PathVariable("evaluatorId") String evaluatorId
|
||||
) throws InvalidApplicationException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
|
||||
logger.debug(new MapLogEntry("get logo" + EvaluatorConfiguration.class.getSimpleName()).And("evaluatorId", evaluatorId));
|
||||
|
||||
String logo = this.evaluatorService.getLogo(evaluatorId);
|
||||
this.auditService.track(AuditableAction.Deposit_GetLogo, Map.ofEntries(
|
||||
new AbstractMap.SimpleEntry<String, Object>("evaluatorId", evaluatorId)
|
||||
));
|
||||
|
||||
return logo;
|
||||
}
|
||||
}
|
|
@ -2589,6 +2589,24 @@ public final class SwaggerHelpers {
|
|||
|
||||
}
|
||||
|
||||
public static final class Evaluator {
|
||||
|
||||
public static final String endpoint_get_available_evaluators =
|
||||
"""
|
||||
This endpoint is used to fetch all the available evaluators.</br>
|
||||
""";
|
||||
|
||||
public static final String endpoint_rank_plans =
|
||||
"""
|
||||
This endpoint is used to rank a plan using a specific evaluator.</br>
|
||||
""";
|
||||
|
||||
public static final String endpoint_rank_descriptions =
|
||||
"""
|
||||
This endpoint is used to rank a description using a specific evaluator.</br>
|
||||
""";
|
||||
}
|
||||
|
||||
public static final class EntityDoi {
|
||||
|
||||
public static final String endpoint_query =
|
||||
|
|
|
@ -29,6 +29,7 @@ spring:
|
|||
optional:classpath:config/public-api.yml[.yml], optional:classpath:config/public-api-${spring.profiles.active}.yml[.yml], optional:file:../config/public-api-${spring.profiles.active}.yml[.yml],
|
||||
optional:classpath:config/dashboard.yml[.yml], optional:classpath:config/dashboard-${spring.profiles.active}.yml[.yml], optional:file:../config/dashboard-${spring.profiles.active}.yml[.yml],
|
||||
optional:classpath:config/file-transformer.yml[.yml], optional:classpath:config/file-transformer-${spring.profiles.active}.yml[.yml], optional:file:../config/file-transformer-${spring.profiles.active}.yml[.yml],
|
||||
optional:classpath:config/evaluator.yml[.yml], optional:classpath:config/evaluator-${spring.profiles.active}.yml[.yml], optional:file:../config/evaluator-${spring.profiles.active}.yml[.yml],
|
||||
optional:classpath:config/authorization.yml[.yml], optional:classpath:config/authorization-${spring.profiles.active}.yml[.yml], optional:file:../config/authorization-${spring.profiles.active}.yml[.yml],
|
||||
optional:classpath:config/metrics.yml[.yml], optional:classpath:config/metrics-${spring.profiles.active}.yml[.yml], optional:file:../config/metrics-${spring.profiles.active}.yml[.yml],
|
||||
optional:classpath:config/field-set-expander.yml[.yml], optional:classpath:config/field-set-expander-${spring.profiles.active}.yml[.yml], optional:file:../config/field-set-expander-${spring.profiles.active}.yml[.yml],
|
||||
|
|
|
@ -50,12 +50,24 @@ cache:
|
|||
maximumSize: 500
|
||||
enableRecordStats: false
|
||||
expireAfterWriteSeconds: 600
|
||||
- names: [ "evaluatorConfigById" ]
|
||||
allowNullValues: true
|
||||
initialCapacity: 100
|
||||
maximumSize: 500
|
||||
enableRecordStats: false
|
||||
expireAfterWriteSeconds: 600
|
||||
- names: [ "fileTransformerSourcesByTenant" ]
|
||||
allowNullValues: true
|
||||
initialCapacity: 100
|
||||
maximumSize: 500
|
||||
enableRecordStats: false
|
||||
expireAfterWriteSeconds: 600
|
||||
- names: [ "evaluatorSourcesByTenant" ]
|
||||
allowNullValues: true
|
||||
initialCapacity: 100
|
||||
maximumSize: 500
|
||||
enableRecordStats: false
|
||||
expireAfterWriteSeconds: 600
|
||||
- names: [ "tokenExchangeKey" ]
|
||||
allowNullValues: true
|
||||
initialCapacity: 100
|
||||
|
@ -126,9 +138,15 @@ cache:
|
|||
fileTransformerConfigById:
|
||||
name: fileTransformerConfigById
|
||||
keyPattern: file_transformer_config_by_id_$transformerId$_$tenantCode$:v0
|
||||
evaluatorConfigById:
|
||||
name: evaluatorConfigById
|
||||
keyPattern: evaluator_config_by_id_$evaluatorId$_$tenantCode$:v0
|
||||
fileTransformerSourcesByTenant:
|
||||
name: fileTransformerSourcesByTenant
|
||||
keyPattern: ile_transformer_sources_by_tenant_$tenantCode$:v0
|
||||
evaluatorSourcesByTenant:
|
||||
name: evaluatorSourcesByTenant
|
||||
keyPattern: evaluator_sources_by_tenant_$tenantCode$:v0
|
||||
token-exchange-key:
|
||||
name: tokenExchangeKey
|
||||
keyPattern: resolve_$keyhash$:v0
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
evaluator:
|
||||
sources:
|
||||
- url: http://localhost:8084
|
||||
evaluatorId: fair
|
||||
issuer-url: ${IDP_ISSUER_URI_TOKEN}
|
||||
client-id: ${IDP_APIKEY_CLIENT_ID}
|
||||
client-secret: ${IDP_APIKEY_CLIENT_SECRET}
|
||||
scope: ${IDP_APIKEY_SCOPE}
|
||||
maxInMemorySizeInBytes: 6554000
|
|
@ -0,0 +1,2 @@
|
|||
evaluator:
|
||||
sources: []
|
|
@ -371,6 +371,17 @@ permissions:
|
|||
clients: [ ]
|
||||
allowAnonymous: false
|
||||
allowAuthenticated: false
|
||||
EvaluateDescription:
|
||||
roles:
|
||||
- Admin
|
||||
- TenantAdmin
|
||||
plan:
|
||||
roles:
|
||||
- Owner
|
||||
claims: [ ]
|
||||
clients: [ ]
|
||||
allowAnonymous: false
|
||||
allowAuthenticated: false
|
||||
CloneDescription:
|
||||
roles:
|
||||
- Admin
|
||||
|
@ -658,6 +669,17 @@ permissions:
|
|||
clients: [ ]
|
||||
allowAnonymous: false
|
||||
allowAuthenticated: false
|
||||
EvaluatePlan:
|
||||
roles:
|
||||
- Admin
|
||||
- TenantAdmin
|
||||
plan:
|
||||
roles:
|
||||
- Owner
|
||||
claims: [ ]
|
||||
clients: [ ]
|
||||
allowAnonymous: false
|
||||
allowAuthenticated: false
|
||||
ClonePlan:
|
||||
roles:
|
||||
- Admin
|
||||
|
|
|
@ -28,8 +28,8 @@
|
|||
"cookieconsent": "^3.1.1",
|
||||
"dragula": "^3.7.3",
|
||||
"file-saver": "^2.0.5",
|
||||
"keycloak-angular": "^15.2.1",
|
||||
"keycloak-js": "^24.0.5",
|
||||
"keycloak-angular": "^16.0.1",
|
||||
"keycloak-js": "^25.0.0",
|
||||
"moment": "^2.30.1",
|
||||
"moment-timezone": "^0.5.45",
|
||||
"ng-dialog-animation": "^9.0.4",
|
||||
|
@ -8856,23 +8856,23 @@
|
|||
}
|
||||
},
|
||||
"node_modules/keycloak-angular": {
|
||||
"version": "15.2.1",
|
||||
"resolved": "https://registry.npmjs.org/keycloak-angular/-/keycloak-angular-15.2.1.tgz",
|
||||
"integrity": "sha512-7w8bkJQ9OBtBJt5eNfqnRG2IL9btvp8Stf2fpVipSE1C/qtd5UQ31skx735PMPgMTUFsdz/0VA32Gmsng54+Xg==",
|
||||
"version": "16.0.1",
|
||||
"resolved": "https://registry.npmjs.org/keycloak-angular/-/keycloak-angular-16.0.1.tgz",
|
||||
"integrity": "sha512-ytkL32R/tfHEyZ3txQtgH1y0WofW/D36zTbo2agDCYUtZETq0wAQ3E/4bVDUAr6ZKwotgAnIyOORfErnvDkXng==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^17",
|
||||
"@angular/core": "^17",
|
||||
"@angular/router": "^17",
|
||||
"keycloak-js": "^18 || ^19 || ^20 || ^21 || ^22 || ^23 || ^24"
|
||||
"@angular/common": "^18",
|
||||
"@angular/core": "^18",
|
||||
"@angular/router": "^18",
|
||||
"keycloak-js": "^18 || ^19 || ^20 || ^21 || ^22 || ^23 || ^24 || ^25"
|
||||
}
|
||||
},
|
||||
"node_modules/keycloak-js": {
|
||||
"version": "24.0.5",
|
||||
"resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-24.0.5.tgz",
|
||||
"integrity": "sha512-VQOSn3j13DPB6OuavKAq+sRjDERhIKrXgBzekoHRstifPuyULILguugX6yxRUYFSpn3OMYUXmSX++tkdCupOjA==",
|
||||
"version": "25.0.6",
|
||||
"resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-25.0.6.tgz",
|
||||
"integrity": "sha512-Km+dc+XfNvY6a4az5jcxTK0zPk52ns9mAxLrHj7lF3V+riVYvQujfHmhayltJDjEpSOJ4C8a57LFNNKnNnRP2g==",
|
||||
"dependencies": {
|
||||
"js-sha256": "^0.11.0",
|
||||
"jwt-decode": "^4.0.0"
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
export enum EvaluatorEntityType {
|
||||
Plan = 0,
|
||||
Description = 1
|
||||
}
|
|
@ -84,6 +84,7 @@ export enum AppPermission {
|
|||
AssignPlanUsers = "AssignPlanUsers",
|
||||
InvitePlanUsers = "InvitePlanUsers",
|
||||
AnnotatePlan = "AnnotatePlan",
|
||||
EvaluatePlan = "EvaluatePlan",
|
||||
|
||||
//PlanStatus
|
||||
BrowsePlanStatus = "BrowsePlanStatus",
|
||||
|
@ -121,6 +122,7 @@ export enum AppPermission {
|
|||
DeleteDescription = "DeleteDescription",
|
||||
CloneDescription = "CloneDescription",
|
||||
ExportDescription = "ExportDescription",
|
||||
EvaluateDescription = "EvaluateDescription",
|
||||
|
||||
//DescriptionTag
|
||||
BrowseDescriptionTag = "BrowseDescriptionTag",
|
||||
|
|
|
@ -52,6 +52,7 @@ import { PlanStatusService } from './services/plan/plan-status.service';
|
|||
import { DescriptionStatusService } from './services/description-status/description-status.service';
|
||||
import { PlanWorkflowService } from './services/plan/plan-workflow.service';
|
||||
import { DescriptionWorkflowService } from './services/description-workflow/description-workflow.service';
|
||||
import { EvaluatorHttpService } from './services/evaluator/evaluator.http.service';
|
||||
//
|
||||
//
|
||||
// This is shared module that provides all the services. Its imported only once on the AppModule.
|
||||
|
@ -112,6 +113,7 @@ export class CoreServiceModule {
|
|||
CanDeactivateGuard,
|
||||
FileTransformerService,
|
||||
FileTransformerHttpService,
|
||||
EvaluatorHttpService,
|
||||
SemanticsService,
|
||||
PrefillingSourceService,
|
||||
VisibilityRulesService,
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import { RankType } from "./rank-type";
|
||||
import { EvaluatorEntityType } from "@app/core/common/enum/evaluator-entity-type";
|
||||
import { RankConfig } from "./rank-config";
|
||||
|
||||
export class EvaluatorConfiguration{
|
||||
evaluatorId: string;
|
||||
rankType: RankType[];
|
||||
evaluatorEntityTypes: EvaluatorEntityType[];
|
||||
rankConfig: RankConfig[];
|
||||
hasLogo: boolean;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import { EvaluatorEntityType } from "@app/core/common/enum/evaluator-entity-type";
|
||||
import { RankType } from "./rank-type";
|
||||
import { SelectionConfiguration } from "./evaluator-selection";
|
||||
import { ValueRangeConfiguration } from "./evaluator-value-range";
|
||||
|
||||
export interface EvaluatorFormat {
|
||||
rankType: RankType[];
|
||||
selectionConfiguration: SelectionConfiguration;
|
||||
valueRangeConfiguration: ValueRangeConfiguration;
|
||||
evaluatorId: string;
|
||||
entityTypes: EvaluatorEntityType[];
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
export enum NumberType {
|
||||
Decimal = 0,
|
||||
Integer = 1
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
export class RankModel {
|
||||
rank: number;
|
||||
details: string;
|
||||
messages: { [key: string]: string };
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import { ValueSet } from "./evaluator-value-set";
|
||||
|
||||
export class SelectionConfiguration {
|
||||
valueSetList: ValueSet[];
|
||||
|
||||
constructor(valueSetList: ValueSet[]) {
|
||||
this.valueSetList = valueSetList;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
export enum SuccessStatus {
|
||||
Fail = 0,
|
||||
Pass = 1
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import { NumberType } from "./evaluator-number-type.model";
|
||||
|
||||
export class ValueRangeConfiguration {
|
||||
numberType: NumberType;
|
||||
min: number;
|
||||
max: number;
|
||||
minPassValue: number;
|
||||
|
||||
constructor(numberType: NumberType, min: number, max: number, minPassValue: number) {
|
||||
this.numberType = numberType;
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
this.minPassValue = minPassValue;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import { SuccessStatus } from "./evaluator-success-status.model";
|
||||
|
||||
export class ValueSet {
|
||||
key: number;
|
||||
successStatus: SuccessStatus;
|
||||
|
||||
constructor(key: number, successStatus: SuccessStatus) {
|
||||
this.key = key;
|
||||
this.successStatus = successStatus;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import { RankType } from "./rank-type";
|
||||
import { ValueRangeConfiguration } from "./evaluator-value-range";
|
||||
import { SelectionConfiguration } from "./evaluator-selection";
|
||||
|
||||
export class RankConfig{
|
||||
rankType: RankType;
|
||||
valueRangeConfiguration?: ValueRangeConfiguration;
|
||||
selectionConfiguration?: SelectionConfiguration;
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
export enum RankType {
|
||||
ValueRange = 0,
|
||||
Selection = 1
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
import { HttpHeaders } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BaseService } from '@common/base/base.service';
|
||||
import { Guid } from '@common/types/guid';
|
||||
import { Observable, throwError } from 'rxjs';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
import { ConfigurationService } from '../configuration/configuration.service';
|
||||
import { BaseHttpV2Service } from '../http/base-http-v2.service';
|
||||
import { EvaluatorFormat } from '@app/core/model/evaluator/evaluator-format.model';
|
||||
import { RankModel } from '@app/core/model/evaluator/evaluator-plan-model.model';
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class EvaluatorHttpService extends BaseService {
|
||||
|
||||
private headers = new HttpHeaders();
|
||||
|
||||
constructor(
|
||||
private http: BaseHttpV2Service,
|
||||
private configurationService: ConfigurationService
|
||||
) { super(); }
|
||||
|
||||
private get apiBase(): string { return `${this.configurationService.server}evaluator`; }
|
||||
|
||||
getAvailableConfigurations(): Observable<EvaluatorFormat[]> {
|
||||
const url = `${this.apiBase}/available`;
|
||||
return this.http.get<EvaluatorFormat[]>(url).pipe(catchError((error: any) => throwError(error)));
|
||||
}
|
||||
|
||||
rankPlan(id: Guid, evaluatorId: string, format: string): Observable<RankModel> {
|
||||
const url = `${this.apiBase}/rank-plan`;
|
||||
return this.http.post<RankModel>(url, {id: id, evaluatorId: evaluatorId, format: format}, {responseType: 'json', observe: 'response'}).pipe(catchError((error: any) => throwError(error)));
|
||||
}
|
||||
|
||||
rankDescription(id: Guid, evaluatorId: string, format: string): Observable<RankModel> {
|
||||
const url = `${this.apiBase}/rank-description`;
|
||||
return this.http.post<RankModel>(url, {id: id, evaluatorId: evaluatorId, format: format}, {responseType: 'json', observe: 'response'}).pipe(catchError((error: any) => throwError(error)));
|
||||
}
|
||||
|
||||
getLogo(evaluatorId: string): Observable<string>{
|
||||
const url = `${this.apiBase}/${evaluatorId}/logo`;
|
||||
return this.http.get<string>(url).pipe(catchError((error: any) => throwError(error)));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
import { Component, EventEmitter, Input, OnInit, Output, Injectable } from '@angular/core';
|
||||
import { BaseService } from '@common/base/base.service';
|
||||
import { catchError, takeUntil } from 'rxjs/operators';
|
||||
import { EvaluatorHttpService } from './evaluator.http.service';
|
||||
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
import { EvaluatorEntityType } from '@app/core/common/enum/evaluator-entity-type';
|
||||
import { EvaluatorConfiguration } from '@app/core/model/evaluator/evaluator-configuration';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Guid } from '@common/types/guid';
|
||||
import {
|
||||
SnackBarNotificationLevel,
|
||||
UiNotificationService
|
||||
} from '@app/core/services/notification/ui-notification-service';
|
||||
import { Observable, throwError } from 'rxjs';
|
||||
import { tap, share } from 'rxjs/operators';
|
||||
import { RankModel } from '@app/core/model/evaluator/evaluator-plan-model.model';
|
||||
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class EvaluatorService extends BaseService {
|
||||
|
||||
constructor(
|
||||
private evaluatorHttpService: EvaluatorHttpService,
|
||||
private authentication: AuthService,
|
||||
private httpErrorHandlingService: HttpErrorHandlingService,
|
||||
private language: TranslateService,
|
||||
private uiNotificationService: UiNotificationService,
|
||||
) { super(); }
|
||||
|
||||
private _initialized: boolean = false;
|
||||
private _loading: boolean = false;
|
||||
|
||||
private _availableEvaluators: EvaluatorConfiguration[] = [];
|
||||
|
||||
get availableEvaluators(): EvaluatorConfiguration[] {
|
||||
if (!this.authentication.currentAccountIsAuthenticated()) {
|
||||
return [];
|
||||
}
|
||||
if (!this._initialized && !this._loading) this.init(); // if not initialized and loading calls init to initialize the evaluators.
|
||||
return this._availableEvaluators;
|
||||
}
|
||||
|
||||
public availableEvaluatorsFor(entityType: EvaluatorEntityType) {
|
||||
// Filter evaluators by entity type
|
||||
// The fetch logo config should be here.
|
||||
if (this.availableEvaluators) {
|
||||
const filteredEvaluators = this.availableEvaluators.filter(x => {
|
||||
return x.evaluatorEntityTypes && x.evaluatorEntityTypes.includes(entityType);
|
||||
});
|
||||
|
||||
return filteredEvaluators;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
init() {
|
||||
this._loading = true;
|
||||
this.evaluatorHttpService.getAvailableConfigurations()
|
||||
.pipe(takeUntil(this._destroyed), catchError((error) => {
|
||||
this._loading = false;
|
||||
this._initialized = true;
|
||||
this.httpErrorHandlingService.handleBackedRequestError(error);
|
||||
return [];
|
||||
}))
|
||||
.subscribe(items => {
|
||||
this._availableEvaluators = items;
|
||||
this._loading = false;
|
||||
this._initialized = true;
|
||||
});
|
||||
}
|
||||
|
||||
rankPlan(id: Guid, evaluatorId: string, format: string, isPublic: boolean = false): Observable<RankModel> {
|
||||
this._loading = true;
|
||||
|
||||
return this.evaluatorHttpService.rankPlan(id, evaluatorId, format).pipe(
|
||||
tap({
|
||||
next: (doi) => {
|
||||
this.onCallbackSuccess();
|
||||
},
|
||||
error: (error) => {
|
||||
this.onCallbackError(error);
|
||||
// Ensure loading state is turned off in case of error
|
||||
this._loading = false;
|
||||
},
|
||||
complete: () => {
|
||||
this._loading = false;
|
||||
}
|
||||
}),
|
||||
catchError((error) => {
|
||||
// Ensure loading state is turned off in case of error
|
||||
this._loading = false;
|
||||
return throwError(error);
|
||||
}),
|
||||
share()
|
||||
);
|
||||
}
|
||||
|
||||
rankDescription(id: Guid, evaluatorId: string, format: string, isPublic: boolean = false): Observable<RankModel> {
|
||||
this._loading = true;
|
||||
return this.evaluatorHttpService.rankDescription(id, evaluatorId, format)
|
||||
.pipe(
|
||||
takeUntil(this._destroyed),
|
||||
tap(response => {
|
||||
this._loading = false;
|
||||
this.onCallbackSuccess();
|
||||
}),
|
||||
catchError(error => {
|
||||
this._loading = false;
|
||||
this.onCallbackError(error);
|
||||
return throwError(error);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
getLogo(evaluatorId: string): Observable<string> {
|
||||
return this.evaluatorHttpService.getLogo(evaluatorId).pipe(
|
||||
catchError((error) => {
|
||||
this.httpErrorHandlingService.handleBackedRequestError(error);
|
||||
return throwError(error);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
onCallbackSuccess(): void {
|
||||
this.uiNotificationService.snackBarNotification(this.language.instant('PLAN-EDITOR.SNACK-BAR.SUCCESSFUL-EVALUATION'), SnackBarNotificationLevel.Success);
|
||||
}
|
||||
|
||||
onCallbackError(error) {
|
||||
this.uiNotificationService.snackBarNotification(error.error.message ? error.error.message : this.language.instant('PLAN-EDITOR.SNACK-BAR.UNSUCCESSFUL-EVALUATION'), SnackBarNotificationLevel.Error);
|
||||
}
|
||||
|
||||
}
|
|
@ -3,6 +3,7 @@ import { FormattingModule } from '@app/core/formatting.module';
|
|||
import { DescriptionRoutingModule, PublicDescriptionRoutingModule } from '@app/ui/description/description.routing';
|
||||
import { CommonFormsModule } from '@common/forms/common-forms.module';
|
||||
import { CommonUiModule } from '@common/ui/common-ui.module';
|
||||
import { EvaluateDescriptionDialogModule } from './evaluate-description-dialog/evaluate-description-dialog.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -24,6 +25,7 @@ export class DescriptionModule { }
|
|||
CommonFormsModule,
|
||||
FormattingModule,
|
||||
PublicDescriptionRoutingModule,
|
||||
EvaluateDescriptionDialogModule,
|
||||
],
|
||||
declarations: [
|
||||
],
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
<h1 mat-dialog-title>{{ 'DESCRIPTION-EVALUATE-DIALOG.HEADER' | translate }}</h1>
|
||||
<div mat-dialog-content>
|
||||
<mat-card>
|
||||
<mat-card-header>
|
||||
<mat-card-title>{{'DESCRIPTION-EVALUATE-DIALOG.DETAILS-SUB-HEADER' | translate}}</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<div *ngIf="data.rankData" class="dialog-content">
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>{{'DESCRIPTION-EVALUATE-DIALOG.RANK-BODY' | translate}}</mat-label>
|
||||
<input matInput [value]="data.rankData.body.rank" disabled>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>{{'DESCRIPTION-EVALUATE-DIALOG.DETAILS-BODY' | translate}}</mat-label>
|
||||
<textarea matInput [value]="data.rankData.body.details" rows="4" disabled></textarea>
|
||||
</mat-form-field>
|
||||
|
||||
<div *ngIf="data.rankData.body.messages">
|
||||
<mat-card-header>
|
||||
<mat-card-title>{{'DESCRIPTION-EVALUATE-DIALOG.MESSAGES-BODY' | translate}}</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card>
|
||||
<mat-card-content>
|
||||
<ul>
|
||||
<li *ngFor="let entry of data.rankData.body.messages | keyvalue">
|
||||
<strong>{{ entry.key }}:</strong> {{ entry.value }}
|
||||
</li>
|
||||
</ul>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
|
@ -0,0 +1,8 @@
|
|||
.dialog-content {
|
||||
display: flex;
|
||||
flex-direction: column; /* Stack form fields vertically */
|
||||
}
|
||||
|
||||
mat-form-field {
|
||||
margin-bottom: 16px; /* Space between fields */
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { EvaluateDescriptionDialogComponent } from './evaluate-description-dialog.component';
|
||||
|
||||
describe('EvaluateDescriptionDialogComponent', () => {
|
||||
let component: EvaluateDescriptionDialogComponent;
|
||||
let fixture: ComponentFixture<EvaluateDescriptionDialogComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [EvaluateDescriptionDialogComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(EvaluateDescriptionDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
import { Component, Inject } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { RankModel } from '@app/core/model/evaluator/evaluator-plan-model.model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-evaluate-description-dialog',
|
||||
templateUrl: './evaluate-description-dialog.component.html',
|
||||
styleUrl: './evaluate-description-dialog.component.scss'
|
||||
})
|
||||
export class EvaluateDescriptionDialogComponent {
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<EvaluateDescriptionDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: { rankData: RankModel }
|
||||
) { }
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { NgIf } from '@angular/common';
|
||||
import { EvaluateDescriptionDialogComponent } from './evaluate-description-dialog.component';
|
||||
import { CommonUiModule } from '@common/ui/common-ui.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatCardModule,
|
||||
MatButtonModule,
|
||||
MatInputModule,
|
||||
MatFormFieldModule,
|
||||
NgIf,
|
||||
CommonUiModule
|
||||
],
|
||||
declarations: [EvaluateDescriptionDialogComponent],
|
||||
exports: [EvaluateDescriptionDialogComponent]
|
||||
})
|
||||
export class EvaluateDescriptionDialogModule { }
|
|
@ -168,6 +168,28 @@
|
|||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="evaluatorService.availableEvaluatorsFor(evaluatorEntityTypeEnum.Description).length > 0">
|
||||
<div class="row mb-3 align-items-center">
|
||||
<div class="col-auto pr-0">
|
||||
<button mat-mini-fab class="frame-btn" [matMenuTriggerFor]="rankMenu">
|
||||
<mat-icon class="mat-mini-fab-icon">open_in_new</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-auto pl-0">
|
||||
<p class="mb-0 mr-0 pl-2 frame-txt" [matMenuTriggerFor]="rankMenu">{{ 'DESCRIPTION-OVERVIEW.ACTIONS.EVALUATE' | translate }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<mat-menu #rankMenu="matMenu" xPosition="before">
|
||||
<button mat-menu-item *ngFor='let evaluator of evaluatorService.availableEvaluatorsFor(evaluatorEntityTypeEnum.Description)'
|
||||
(click)="onEvaluateDescription(description.id, evaluator.evaluatorId, evaluator.format, isPublicView)">
|
||||
<span class="evaluator-id pr-2">{{ (evaluator.evaluatorId?.toUpperCase()) | translate }}</span>
|
||||
<img *ngIf="evaluator.hasLogo" class="logo" [src]="logos.get(evaluator.evaluatorId)">
|
||||
<img *ngIf="!evaluator.hasLogo" class="logo" src="assets/images/repository-placeholder.png">
|
||||
</button>
|
||||
</mat-menu>
|
||||
|
||||
<mat-menu #exportMenu="matMenu" xPosition="before">
|
||||
<button mat-menu-item *ngFor='let fileTransformer of fileTransformerService.availableFormatsFor(fileTransformerEntityTypeEnum.Description)' (click)="fileTransformerService.exportDescription(description.id, fileTransformer.repositoryId, fileTransformer.format, isPublicView)">
|
||||
<i class="fa pr-2" [ngClass]="fileTransformer.icon ? fileTransformer.icon : 'fa-file-o'"></i>
|
||||
|
|
|
@ -274,3 +274,8 @@
|
|||
.deleted-item {
|
||||
color: #cf1407;
|
||||
}
|
||||
.logo {
|
||||
margin-right: 16px;
|
||||
max-width: 24px;
|
||||
max-height: 24px;
|
||||
}
|
|
@ -2,17 +2,24 @@ import { Location } from '@angular/common';
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { UntypedFormBuilder, Validators } from '@angular/forms';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
|
||||
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||
import { DescriptionStatusEnum } from '@app/core/common/enum/description-status';
|
||||
import { PlanAccessType } from '@app/core/common/enum/plan-access-type';
|
||||
import { PlanStatusEnum } from '@app/core/common/enum/plan-status';
|
||||
import { PlanUserRole } from '@app/core/common/enum/plan-user-role';
|
||||
import { DescriptionStatusAvailableActionType } from '@app/core/common/enum/description-status-available-action-type';
|
||||
import { DescriptionStatusPermission } from '@app/core/common/enum/description-status-permission.enum';
|
||||
import { EvaluatorEntityType } from '@app/core/common/enum/evaluator-entity-type';
|
||||
import { FileTransformerEntityType } from '@app/core/common/enum/file-transformer-entity-type';
|
||||
import { IsActive } from '@app/core/common/enum/is-active.enum';
|
||||
import { AppPermission } from '@app/core/common/enum/permission.enum';
|
||||
import { PlanAccessType } from '@app/core/common/enum/plan-access-type';
|
||||
import { PlanStatusEnum } from '@app/core/common/enum/plan-status';
|
||||
import { PlanUserRole } from '@app/core/common/enum/plan-user-role';
|
||||
import { DescriptionStatus, DescriptionStatusDefinition } from '@app/core/model/description-status/description-status';
|
||||
import { DescriptionTemplate } from '@app/core/model/description-template/description-template';
|
||||
import { BaseDescription, Description, DescriptionStatusPersist, PublicDescription } from '@app/core/model/description/description';
|
||||
import { RankModel } from '@app/core/model/evaluator/evaluator-plan-model.model';
|
||||
import { PlanBlueprint, PlanBlueprintDefinition, PlanBlueprintDefinitionSection } from '@app/core/model/plan-blueprint/plan-blueprint';
|
||||
import { PlanStatus } from '@app/core/model/plan-status/plan-status';
|
||||
import { Plan, PlanDescriptionTemplate, PlanUser, PlanUserRemovePersist } from '@app/core/model/plan/plan';
|
||||
import { PlanReference } from '@app/core/model/plan/plan-reference';
|
||||
import { ReferenceType } from '@app/core/model/reference-type/reference-type';
|
||||
|
@ -21,20 +28,23 @@ import { User } from '@app/core/model/user/user';
|
|||
import { AuthService } from '@app/core/services/auth/auth.service';
|
||||
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
|
||||
import { DescriptionService } from '@app/core/services/description/description.service';
|
||||
import { PlanService } from '@app/core/services/plan/plan.service';
|
||||
import { EvaluatorService } from '@app/core/services/evaluator/evaluator.service';
|
||||
import { FileTransformerService } from '@app/core/services/file-transformer/file-transformer.service';
|
||||
import { LockService } from '@app/core/services/lock/lock.service';
|
||||
import { LoggingService } from '@app/core/services/logging/logging-service';
|
||||
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
|
||||
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
|
||||
import { PlanService } from '@app/core/services/plan/plan.service';
|
||||
import { ReferenceTypeService } from '@app/core/services/reference-type/reference-type.service';
|
||||
import { ReferenceService } from '@app/core/services/reference/reference.service';
|
||||
import { RouterUtilsService } from '@app/core/services/router/router-utils.service';
|
||||
import { UserService } from '@app/core/services/user/user.service';
|
||||
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
|
||||
import { FileUtils } from '@app/core/services/utilities/file-utils.service';
|
||||
import { PopupNotificationDialogComponent } from '@app/library/notification/popup/popup-notification.component';
|
||||
import { DescriptionValidationOutput } from '@app/ui/plan/plan-finalize-dialog/plan-finalize-dialog.component';
|
||||
import { PlanInvitationDialogComponent } from '@app/ui/plan/invitation/dialog/plan-invitation-dialog.component';
|
||||
import { BreadcrumbService } from '@app/ui/misc/breadcrumb/breadcrumb.service';
|
||||
import { PlanInvitationDialogComponent } from '@app/ui/plan/invitation/dialog/plan-invitation-dialog.component';
|
||||
import { DescriptionValidationOutput } from '@app/ui/plan/plan-finalize-dialog/plan-finalize-dialog.component';
|
||||
import { BaseComponent } from '@common/base/base.component';
|
||||
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
|
||||
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
|
||||
|
@ -43,11 +53,7 @@ import { TranslateService } from '@ngx-translate/core';
|
|||
import { map, takeUntil } from 'rxjs/operators';
|
||||
import { nameof } from 'ts-simple-nameof';
|
||||
import { DescriptionCopyDialogComponent } from '../description-copy-dialog/description-copy-dialog.component';
|
||||
import { RouterUtilsService } from '@app/core/services/router/router-utils.service';
|
||||
import { DescriptionStatus, DescriptionStatusDefinition } from '@app/core/model/description-status/description-status';
|
||||
import { PlanStatus } from '@app/core/model/plan-status/plan-status';
|
||||
import { DescriptionStatusAvailableActionType } from '@app/core/common/enum/description-status-available-action-type';
|
||||
import { DescriptionStatusPermission } from '@app/core/common/enum/description-status-permission.enum';
|
||||
import { EvaluateDescriptionDialogComponent } from './../evaluate-description-dialog/evaluate-description-dialog.component';
|
||||
|
||||
|
||||
@Component({
|
||||
|
@ -70,6 +76,8 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
|
|||
planStatusEnum = PlanStatusEnum;
|
||||
planUserRoleEnum = PlanUserRole;
|
||||
fileTransformerEntityTypeEnum = FileTransformerEntityType;
|
||||
evaluatorEntityTypeEnum = EvaluatorEntityType;
|
||||
logos: Map<string, SafeResourceUrl> = new Map<string, SafeResourceUrl>();
|
||||
|
||||
canEdit = false;
|
||||
canCopy = false;
|
||||
|
@ -77,10 +85,12 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
|
|||
canFinalize = false;
|
||||
canAnnotate = false;
|
||||
canInvitePlanUsers = false;
|
||||
get canAssignPlanUsers(): boolean {
|
||||
const authorizationFlags = !this.isPublicView ? (this.description?.plan as Plan)?.authorizationFlags : [];
|
||||
canEvaluate = false;
|
||||
|
||||
get canAssignPlanUsers(): boolean {
|
||||
const authorizationFlags = !this.isPublicView ? (this.description?.plan as Plan)?.authorizationFlags : [];
|
||||
return (authorizationFlags?.some(x => x === AppPermission.InvitePlanUsers) || this.authentication.hasPermission(AppPermission.InvitePlanUsers)) &&
|
||||
!this.isPublicView && this.description?.belongsToCurrentTenant && this.isActive && (this.description?.plan?.status?.internalStatus == null || this.description?.plan?.status?.internalStatus != PlanStatusEnum.Finalized);
|
||||
!this.isPublicView && this.description?.belongsToCurrentTenant && this.isActive && (this.description?.plan?.status?.internalStatus == null || this.description?.plan?.status?.internalStatus != PlanStatusEnum.Finalized);
|
||||
}
|
||||
|
||||
authorFocus: string;
|
||||
|
@ -110,6 +120,9 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
|
|||
private breadcrumbService: BreadcrumbService,
|
||||
private httpErrorHandlingService: HttpErrorHandlingService,
|
||||
private userService: UserService,
|
||||
private evaluatorService: EvaluatorService,
|
||||
private logger: LoggingService,
|
||||
private sanitizer: DomSanitizer,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
@ -122,6 +135,7 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
|
|||
this.canCopy = false;
|
||||
this.canFinalize = false;
|
||||
this.canInvitePlanUsers = false;
|
||||
this.canEvaluate = false;
|
||||
// Gets description data using parameter id
|
||||
this.route.params
|
||||
.pipe(takeUntil(this._destroyed))
|
||||
|
@ -134,43 +148,46 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
|
|||
this.descriptionService.getSingle(itemId, this.lookupFields())
|
||||
.pipe(takeUntil(this._destroyed))
|
||||
.subscribe(
|
||||
{
|
||||
next: (data) => {
|
||||
this.breadcrumbService.addIdResolvedValue(data.id.toString(), data.label);
|
||||
{
|
||||
next: (data) => {
|
||||
this.breadcrumbService.addIdResolvedValue(data.id.toString(), data.label);
|
||||
|
||||
this.description = data;
|
||||
this.description.plan.planUsers = this.isActive || this.description.plan.isActive === IsActive.Active ? data.plan.planUsers.filter(x => x.isActive === IsActive.Active) : data.plan.planUsers;
|
||||
this.researchers = this.referenceService.getReferencesForTypes(this.description?.plan?.planReferences, [this.referenceTypeService.getResearcherReferenceType()]);
|
||||
this.checkLockStatus(this.description.id);
|
||||
this.canDelete = this.isActive && (this.authService.hasPermission(AppPermission.DeleteDescription) ||
|
||||
this.description.authorizationFlags?.some(x => x === AppPermission.DeleteDescription)) && this.description.belongsToCurrentTenant != false;
|
||||
this.description = data;
|
||||
this.description.plan.planUsers = this.isActive || this.description.plan.isActive === IsActive.Active ? data.plan.planUsers.filter(x => x.isActive === IsActive.Active) : data.plan.planUsers;
|
||||
this.researchers = this.referenceService.getReferencesForTypes(this.description?.plan?.planReferences, [this.referenceTypeService.getResearcherReferenceType()]);
|
||||
this.checkLockStatus(this.description.id);
|
||||
this.canDelete = this.isActive && (this.authService.hasPermission(AppPermission.DeleteDescription) ||
|
||||
this.description.authorizationFlags?.some(x => x === AppPermission.DeleteDescription)) && this.description.belongsToCurrentTenant != false;
|
||||
|
||||
this.canEdit = (this.authService.hasPermission(AppPermission.EditDescription) ||
|
||||
this.description.authorizationFlags?.some(x => x === AppPermission.EditDescription)) && this.description.belongsToCurrentTenant != false;
|
||||
this.canEdit = (this.authService.hasPermission(AppPermission.EditDescription) ||
|
||||
this.description.authorizationFlags?.some(x => x === AppPermission.EditDescription)) && this.description.belongsToCurrentTenant != false;
|
||||
|
||||
this.canCopy = this.canEdit || (this.authService.hasPermission(AppPermission.PublicCloneDescription) && this.isPublicView);
|
||||
this.canCopy = this.canEdit || (this.authService.hasPermission(AppPermission.PublicCloneDescription) && this.isPublicView);
|
||||
|
||||
this.canAnnotate = (this.authService.hasPermission(AppPermission.AnnotateDescription) ||
|
||||
this.description.authorizationFlags?.some(x => x === AppPermission.AnnotateDescription)) && this.description.belongsToCurrentTenant != false;
|
||||
this.canAnnotate = (this.authService.hasPermission(AppPermission.AnnotateDescription) ||
|
||||
this.description.authorizationFlags?.some(x => x === AppPermission.AnnotateDescription)) && this.description.belongsToCurrentTenant != false;
|
||||
|
||||
this.canFinalize = this.isActive && (this.authService.hasPermission(AppPermission.FinalizeDescription) ||
|
||||
this.description.authorizationFlags?.some(x => x === AppPermission.FinalizeDescription)) && this.description.belongsToCurrentTenant != false;
|
||||
this.canFinalize = this.isActive && (this.authService.hasPermission(AppPermission.FinalizeDescription) ||
|
||||
this.description.authorizationFlags?.some(x => x === AppPermission.FinalizeDescription)) && this.description.belongsToCurrentTenant != false;
|
||||
|
||||
this.canInvitePlanUsers = this.isActive && (this.authService.hasPermission(AppPermission.InvitePlanUsers) ||
|
||||
this.description.authorizationFlags?.some(x => x === AppPermission.InvitePlanUsers)) && this.description.belongsToCurrentTenant != false;
|
||||
this.canInvitePlanUsers = this.isActive && (this.authService.hasPermission(AppPermission.InvitePlanUsers) ||
|
||||
this.description.authorizationFlags?.some(x => x === AppPermission.InvitePlanUsers)) && this.description.belongsToCurrentTenant != false;
|
||||
|
||||
},
|
||||
error: (error: any) => {
|
||||
this.httpErrorHandlingService.handleBackedRequestError(error);
|
||||
this.canEvaluate = this.isActive && (this.authService.hasPermission(AppPermission.EvaluateDescription) ||
|
||||
this.description.authorizationFlags?.some(x => x === AppPermission.EvaluateDescription)) && this.description.belongsToCurrentTenant != false;
|
||||
|
||||
if (error.status === 404) {
|
||||
},
|
||||
error: (error: any) => {
|
||||
this.httpErrorHandlingService.handleBackedRequestError(error);
|
||||
|
||||
if (error.status === 404) {
|
||||
return this.onFetchingDeletedCallbackError('/descriptions/');
|
||||
}
|
||||
if (error.status === 403) {
|
||||
}
|
||||
if (error.status === 403) {
|
||||
return this.onFetchingForbiddenCallbackError('/descriptions/');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
else if (publicId != null) {
|
||||
this.isNew = false;
|
||||
|
@ -179,26 +196,26 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
|
|||
this.descriptionService.getPublicSingle(publicId, this.lookupFields())
|
||||
.pipe(takeUntil(this._destroyed))
|
||||
.subscribe({
|
||||
next: (data) => {
|
||||
this.canCopy = this.authService.hasPermission(AppPermission.PublicCloneDescription) && this.isPublicView;
|
||||
next: (data) => {
|
||||
this.canCopy = this.authService.hasPermission(AppPermission.PublicCloneDescription) && this.isPublicView;
|
||||
|
||||
this.breadcrumbService.addExcludedParam('public', true);
|
||||
this.breadcrumbService.addIdResolvedValue(data.id.toString(), data.label);
|
||||
this.breadcrumbService.addExcludedParam('public', true);
|
||||
this.breadcrumbService.addIdResolvedValue(data.id.toString(), data.label);
|
||||
|
||||
this.description = data;
|
||||
this.researchers = this.referenceService.getReferencesForTypes(this.description?.plan?.planReferences, [this.referenceTypeService.getResearcherReferenceType()]);
|
||||
},
|
||||
error: (error: any) => {
|
||||
this.httpErrorHandlingService.handleBackedRequestError(error);
|
||||
this.description = data;
|
||||
this.researchers = this.referenceService.getReferencesForTypes(this.description?.plan?.planReferences, [this.referenceTypeService.getResearcherReferenceType()]);
|
||||
},
|
||||
error: (error: any) => {
|
||||
this.httpErrorHandlingService.handleBackedRequestError(error);
|
||||
|
||||
if (error.status === 404) {
|
||||
return this.onFetchingDeletedCallbackError('/explore-descriptions');
|
||||
}
|
||||
if (error.status === 403) {
|
||||
return this.onFetchingForbiddenCallbackError('/explore-descriptions');
|
||||
}
|
||||
}
|
||||
});
|
||||
if (error.status === 404) {
|
||||
return this.onFetchingDeletedCallbackError('/explore-descriptions');
|
||||
}
|
||||
if (error.status === 403) {
|
||||
return this.onFetchingForbiddenCallbackError('/explore-descriptions');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -225,33 +242,62 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
|
|||
return this.description?.status?.definition?.availableActions?.filter(x => x === DescriptionStatusAvailableActionType.Export).length > 0;
|
||||
}
|
||||
|
||||
get canEditStatus(): boolean{
|
||||
return (this.description as Description).statusAuthorizationFlags?.some(x => x.toLowerCase() === DescriptionStatusPermission.Edit.toLowerCase())
|
||||
}
|
||||
get canEditStatus(): boolean {
|
||||
return (this.description as Description).statusAuthorizationFlags?.some(x => x.toLowerCase() === DescriptionStatusPermission.Edit.toLowerCase())
|
||||
}
|
||||
|
||||
hasAvailableFinalizeStatus() {
|
||||
return (this.description as Description).availableStatuses?.find(x => x.internalStatus === DescriptionStatusEnum.Finalized) != null;
|
||||
}
|
||||
|
||||
onEvaluateDescription(planId: Guid, evaluatorId: string, format: string, isPublicView: boolean) {
|
||||
this.evaluatorService.rankDescription(planId, evaluatorId, format).subscribe(
|
||||
(response: RankModel) => {
|
||||
this.evaluatorService.getLogo(evaluatorId).subscribe(
|
||||
(logo: string) => {
|
||||
|
||||
this.logos.set(evaluatorId, this.sanitizer.bypassSecurityTrustResourceUrl('data:image/png;base64, ' + logo));
|
||||
|
||||
const dialogRef = this.dialog.open(EvaluateDescriptionDialogComponent, {
|
||||
data: {
|
||||
rankData: response,
|
||||
}
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
this.logger.debug("Dialog closed with result:", result);
|
||||
});
|
||||
},
|
||||
error => {
|
||||
this.logger.error("Error fetching evaluator logo:", error);
|
||||
}
|
||||
);
|
||||
},
|
||||
error => {
|
||||
this.logger.error("Error ranking description:", error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
checkLockStatus(id: Guid) {
|
||||
this.lockService.checkLockStatus(id).pipe(takeUntil(this._destroyed))
|
||||
.subscribe({
|
||||
next: (lockStatus) => {
|
||||
this.isLocked = lockStatus.status;
|
||||
if (this.isLocked) {
|
||||
this.dialog.open(PopupNotificationDialogComponent, {
|
||||
data: {
|
||||
title: this.language.instant('DESCRIPTION-OVERVIEW.LOCKED-DIALOG.TITLE'),
|
||||
message: this.language.instant('DESCRIPTION-OVERVIEW.LOCKED-DIALOG.MESSAGE')
|
||||
}, maxWidth: '30em'
|
||||
});
|
||||
}
|
||||
},
|
||||
error: (error) => {
|
||||
next: (lockStatus) => {
|
||||
this.isLocked = lockStatus.status;
|
||||
if (this.isLocked) {
|
||||
this.dialog.open(PopupNotificationDialogComponent, {
|
||||
data: {
|
||||
title: this.language.instant('DESCRIPTION-OVERVIEW.LOCKED-DIALOG.TITLE'),
|
||||
message: this.language.instant('DESCRIPTION-OVERVIEW.LOCKED-DIALOG.MESSAGE')
|
||||
}, maxWidth: '30em'
|
||||
});
|
||||
}
|
||||
},
|
||||
error: (error) => {
|
||||
this.router.navigate([this.routerUtils.generateUrl('/descriptions')]);
|
||||
this.httpErrorHandlingService.handleBackedRequestError(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onFetchingDeletedCallbackError(redirectRoot: string) {
|
||||
|
@ -336,7 +382,7 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
|
|||
.subscribe({
|
||||
complete: () => this.onDeleteCallbackSuccess(),
|
||||
error: (error) => this.onDeleteCallbackError(error)
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -440,9 +486,9 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
|
|||
};
|
||||
this.planService.removeUser(planUserRemovePersist).pipe(takeUntil(this._destroyed))
|
||||
.subscribe({
|
||||
next: () => this.reloadPage(),
|
||||
error: (error: any) => this.httpErrorHandlingService.handleBackedRequestError(error)
|
||||
})
|
||||
next: () => this.reloadPage(),
|
||||
error: (error: any) => this.httpErrorHandlingService.handleBackedRequestError(error)
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -450,7 +496,7 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
|
|||
persistStatus(status: DescriptionStatus, description: Description) {
|
||||
if (status.internalStatus != null && status.internalStatus === DescriptionStatusEnum.Finalized) {
|
||||
this.finalize(description, status.id);
|
||||
} else if (status.internalStatus != null && description.status.internalStatus === DescriptionStatusEnum.Finalized){
|
||||
} else if (status.internalStatus != null && description.status.internalStatus === DescriptionStatusEnum.Finalized) {
|
||||
this.reverseFinalization(description, status.id);
|
||||
} else {
|
||||
// other statuses
|
||||
|
@ -474,44 +520,44 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
|
|||
|
||||
this.descriptionService.validate([description.id]).pipe(takeUntil(this._destroyed))
|
||||
.subscribe({
|
||||
next: (result) => {
|
||||
if (result[0].result == DescriptionValidationOutput.Invalid) {
|
||||
this.router.navigate([this.routerUtils.generateUrl(['descriptions/edit', description.id.toString(), 'finalize'], '/')]);
|
||||
} else {
|
||||
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
|
||||
restoreFocus: false,
|
||||
data: {
|
||||
message: this.language.instant('DESCRIPTION-OVERVIEW.FINALIZE-DIALOG.TITLE'),
|
||||
confirmButton: this.language.instant('DESCRIPTION-OVERVIEW.FINALIZE-DIALOG.CONFIRM'),
|
||||
cancelButton: this.language.instant('DESCRIPTION-OVERVIEW.FINALIZE-DIALOG.NEGATIVE'),
|
||||
isDeleteConfirmation: false
|
||||
}
|
||||
});
|
||||
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe({
|
||||
next: (result) => {
|
||||
if (result) {
|
||||
const descriptionStatusPersist: DescriptionStatusPersist = {
|
||||
id: description.id,
|
||||
statusId: statusId,
|
||||
hash: description.hash
|
||||
};
|
||||
this.descriptionService.persistStatus(descriptionStatusPersist).pipe(takeUntil(this._destroyed))
|
||||
.subscribe({
|
||||
next: () => {
|
||||
this.reloadPage();
|
||||
this.onUpdateCallbackSuccess()
|
||||
},
|
||||
error: (error: any) => this.onUpdateCallbackError(error)
|
||||
next: (result) => {
|
||||
if (result[0].result == DescriptionValidationOutput.Invalid) {
|
||||
this.router.navigate([this.routerUtils.generateUrl(['descriptions/edit', description.id.toString(), 'finalize'], '/')]);
|
||||
} else {
|
||||
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
|
||||
restoreFocus: false,
|
||||
data: {
|
||||
message: this.language.instant('DESCRIPTION-OVERVIEW.FINALIZE-DIALOG.TITLE'),
|
||||
confirmButton: this.language.instant('DESCRIPTION-OVERVIEW.FINALIZE-DIALOG.CONFIRM'),
|
||||
cancelButton: this.language.instant('DESCRIPTION-OVERVIEW.FINALIZE-DIALOG.NEGATIVE'),
|
||||
isDeleteConfirmation: false
|
||||
}
|
||||
});
|
||||
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe({
|
||||
next: (result) => {
|
||||
if (result) {
|
||||
const descriptionStatusPersist: DescriptionStatusPersist = {
|
||||
id: description.id,
|
||||
statusId: statusId,
|
||||
hash: description.hash
|
||||
};
|
||||
this.descriptionService.persistStatus(descriptionStatusPersist).pipe(takeUntil(this._destroyed))
|
||||
.subscribe({
|
||||
next: () => {
|
||||
this.reloadPage();
|
||||
this.onUpdateCallbackSuccess()
|
||||
},
|
||||
error: (error: any) => this.onUpdateCallbackError(error)
|
||||
|
||||
})
|
||||
}
|
||||
},
|
||||
error: (error) => this.httpErrorHandlingService.handleBackedRequestError(error)
|
||||
})
|
||||
}
|
||||
},
|
||||
error: (error) => this.httpErrorHandlingService.handleBackedRequestError(error)
|
||||
});
|
||||
})
|
||||
}
|
||||
},
|
||||
error: (error) => this.httpErrorHandlingService.handleBackedRequestError(error)
|
||||
})
|
||||
}
|
||||
},
|
||||
error: (error) => this.httpErrorHandlingService.handleBackedRequestError(error)
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
@ -538,21 +584,21 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
|
|||
};
|
||||
this.descriptionService.persistStatus(planUserRemovePersist).pipe(takeUntil(this._destroyed))
|
||||
.subscribe({
|
||||
next: (data) => {
|
||||
this.reloadPage();
|
||||
this.onUpdateCallbackSuccess()
|
||||
},
|
||||
error: (error: any) => {
|
||||
this.onUpdateCallbackError(error)
|
||||
}
|
||||
});
|
||||
next: (data) => {
|
||||
this.reloadPage();
|
||||
this.onUpdateCallbackSuccess()
|
||||
},
|
||||
error: (error: any) => {
|
||||
this.onUpdateCallbackError(error)
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private lookupFields(): string[] {
|
||||
return [
|
||||
nameof<BaseDescription>(x => x.isActive),
|
||||
nameof<BaseDescription>(x => x.isActive),
|
||||
nameof<Description>(x => x.id),
|
||||
nameof<Description>(x => x.label),
|
||||
nameof<Description>(x => x.description),
|
||||
|
@ -569,6 +615,7 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
|
|||
[nameof<Description>(x => x.authorizationFlags), AppPermission.FinalizeDescription].join('.'),
|
||||
[nameof<Description>(x => x.authorizationFlags), AppPermission.InvitePlanUsers].join('.'),
|
||||
[nameof<Description>(x => x.authorizationFlags), AppPermission.AnnotateDescription].join('.'),
|
||||
[nameof<Description>(x => x.authorizationFlags), AppPermission.EvaluateDescription].join('.'),
|
||||
|
||||
[nameof<Description>(x => x.statusAuthorizationFlags), DescriptionStatusPermission.Edit].join('.'),
|
||||
|
||||
|
|
|
@ -228,6 +228,29 @@
|
|||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="canEvaluatePlan() && evaluatorService.availableEvaluatorsFor(evaluatorEntityTypeEnum.Plan).length > 0">
|
||||
<div class="row mb-3 align-items-center">
|
||||
<div class="col-auto pr-0">
|
||||
<button mat-mini-fab class="frame-btn" [matMenuTriggerFor]="rankMenu">
|
||||
<mat-icon class="mat-mini-fab-icon">open_in_new</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-auto pl-0">
|
||||
<p class="mb-0 pl-2 frame-txt" [matMenuTriggerFor]="rankMenu">{{ 'PLAN-OVERVIEW.ACTIONS.EVALUATE' | translate }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<mat-menu #rankMenu="matMenu" xPosition="before">
|
||||
<button mat-menu-item *ngFor='let evaluator of evaluatorService.availableEvaluatorsFor(evaluatorEntityTypeEnum.Plan)'
|
||||
(click)="onEvaluatePlan(plan.id, evaluator.evaluatorId, evaluator.format, isPublicView)">
|
||||
<span class="evaluator-id pr-2">{{ (evaluator.evaluatorId?.toUpperCase()) | translate }}</span>
|
||||
<img *ngIf="evaluator.hasLogo" class="logo" [src]="logos.get(evaluator.evaluatorId)">
|
||||
<img *ngIf="!evaluator.hasLogo" class="logo" src="assets/images/repository-placeholder.png">
|
||||
</button>
|
||||
</mat-menu>
|
||||
|
||||
<mat-menu #exportMenu="matMenu" xPosition="before">
|
||||
<button mat-menu-item *ngFor='let fileTransformer of fileTransformerService.availableFormatsFor(fileTransformerEntityTypeEnum.Plan)' (click)="fileTransformerService.exportPlan(plan.id, fileTransformer.repositoryId, fileTransformer.format, isPublicView)">
|
||||
<i class="fa pr-2" [ngClass]="fileTransformer.icon ? fileTransformer.icon : 'fa-file-o'"></i>
|
||||
|
|
|
@ -300,3 +300,9 @@
|
|||
.deleted-item {
|
||||
color: #cf1407;
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin-right: 16px;
|
||||
max-width: 24px;
|
||||
max-height: 24px;
|
||||
}
|
|
@ -1,36 +1,46 @@
|
|||
import { Location } from '@angular/common';
|
||||
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
|
||||
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||
import { DescriptionStatusEnum } from '@app/core/common/enum/description-status';
|
||||
import { PlanAccessType } from '@app/core/common/enum/plan-access-type';
|
||||
import { PlanStatusEnum } from '@app/core/common/enum/plan-status';
|
||||
import { PlanUserRole } from '@app/core/common/enum/plan-user-role';
|
||||
import { PlanVersionStatus } from '@app/core/common/enum/plan-version-status';
|
||||
import { EvaluatorEntityType } from '@app/core/common/enum/evaluator-entity-type';
|
||||
import { FileTransformerEntityType } from '@app/core/common/enum/file-transformer-entity-type';
|
||||
import { IsActive } from '@app/core/common/enum/is-active.enum';
|
||||
import { AppPermission } from '@app/core/common/enum/permission.enum';
|
||||
import { PlanAccessType } from '@app/core/common/enum/plan-access-type';
|
||||
import { PlanStatusEnum } from '@app/core/common/enum/plan-status';
|
||||
import { PlanStatusAvailableActionType } from '@app/core/common/enum/plan-status-available-action-type';
|
||||
import { PlanStatusPermission } from '@app/core/common/enum/plan-status-permission.enum';
|
||||
import { PlanUserRole } from '@app/core/common/enum/plan-user-role';
|
||||
import { PlanVersionStatus } from '@app/core/common/enum/plan-version-status';
|
||||
import { DepositConfiguration } from '@app/core/model/deposit/deposit-configuration';
|
||||
import { DescriptionStatus } from '@app/core/model/description-status/description-status';
|
||||
import { DescriptionTemplate } from '@app/core/model/description-template/description-template';
|
||||
import { Description } from '@app/core/model/description/description';
|
||||
import { EntityDoi } from '@app/core/model/entity-doi/entity-doi';
|
||||
import { RankModel } from '@app/core/model/evaluator/evaluator-plan-model.model';
|
||||
import { DescriptionTemplatesInSection, PlanBlueprint, PlanBlueprintDefinition, PlanBlueprintDefinitionSection } from '@app/core/model/plan-blueprint/plan-blueprint';
|
||||
import { PlanStatus, PlanStatusDefinition } from '@app/core/model/plan-status/plan-status';
|
||||
import { BasePlan, Plan, PlanDescriptionTemplate, PlanUser, PlanUserRemovePersist, PublicPlan } from '@app/core/model/plan/plan';
|
||||
import { PlanReference } from '@app/core/model/plan/plan-reference';
|
||||
import { EntityDoi } from '@app/core/model/entity-doi/entity-doi';
|
||||
import { ReferenceType } from '@app/core/model/reference-type/reference-type';
|
||||
import { Reference } from '@app/core/model/reference/reference';
|
||||
import { User } from '@app/core/model/user/user';
|
||||
import { AuthService } from '@app/core/services/auth/auth.service';
|
||||
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
|
||||
import { DepositService } from '@app/core/services/deposit/deposit.service';
|
||||
import { PlanBlueprintService } from '@app/core/services/plan/plan-blueprint.service';
|
||||
import { PlanService } from '@app/core/services/plan/plan.service';
|
||||
import { EvaluatorService } from '@app/core/services/evaluator/evaluator.service';
|
||||
import { FileTransformerService } from '@app/core/services/file-transformer/file-transformer.service';
|
||||
import { LockService } from '@app/core/services/lock/lock.service';
|
||||
import { LoggingService } from '@app/core/services/logging/logging-service';
|
||||
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
|
||||
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
|
||||
import { PlanBlueprintService } from '@app/core/services/plan/plan-blueprint.service';
|
||||
import { PlanService } from '@app/core/services/plan/plan.service';
|
||||
import { ReferenceTypeService } from '@app/core/services/reference-type/reference-type.service';
|
||||
import { ReferenceService } from '@app/core/services/reference/reference.service';
|
||||
import { RouterUtilsService } from '@app/core/services/router/router-utils.service';
|
||||
import { UserService } from '@app/core/services/user/user.service';
|
||||
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
|
||||
import { FileUtils } from '@app/core/services/utilities/file-utils.service';
|
||||
|
@ -44,16 +54,11 @@ import { TranslateService } from '@ngx-translate/core';
|
|||
import { map, takeUntil } from 'rxjs/operators';
|
||||
import { nameof } from 'ts-simple-nameof';
|
||||
import { ClonePlanDialogComponent } from '../clone-dialog/plan-clone-dialog.component';
|
||||
import { PlanDeleteDialogComponent } from '../plan-delete-dialog/plan-delete-dialog.component';
|
||||
import { PlanEditorEntityResolver } from '../plan-editor-blueprint/resolvers/plan-editor-enitity.resolver';
|
||||
import { PlanFinalizeDialogComponent, PlanFinalizeDialogOutput } from '../plan-finalize-dialog/plan-finalize-dialog.component';
|
||||
import { PlanInvitationDialogComponent } from '../invitation/dialog/plan-invitation-dialog.component';
|
||||
import { NewVersionPlanDialogComponent } from '../new-version-dialog/plan-new-version-dialog.component';
|
||||
import { RouterUtilsService } from '@app/core/services/router/router-utils.service';
|
||||
import { DescriptionStatus } from '@app/core/model/description-status/description-status';
|
||||
import { PlanStatus, PlanStatusDefinition } from '@app/core/model/plan-status/plan-status';
|
||||
import { PlanStatusAvailableActionType } from '@app/core/common/enum/plan-status-available-action-type';
|
||||
import { PlanStatusPermission } from '@app/core/common/enum/plan-status-permission.enum';
|
||||
import { PlanDeleteDialogComponent } from '../plan-delete-dialog/plan-delete-dialog.component';
|
||||
import { PlanEvaluateDialogComponent } from '../plan-evaluate-dialog/plan-evaluate-dialog.component';
|
||||
import { PlanFinalizeDialogComponent, PlanFinalizeDialogOutput } from '../plan-finalize-dialog/plan-finalize-dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-plan-overview',
|
||||
|
@ -73,6 +78,8 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
|
|||
textMessage: any;
|
||||
selectedModel: EntityDoi;
|
||||
fileTransformerEntityTypeEnum = FileTransformerEntityType;
|
||||
evaluatorEntityTypeEnum = EvaluatorEntityType
|
||||
logos: Map<string, SafeResourceUrl> = new Map<string, SafeResourceUrl>();
|
||||
|
||||
@ViewChild('doi')
|
||||
doi: ElementRef;
|
||||
|
@ -112,6 +119,9 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
|
|||
private breadcrumbService: BreadcrumbService,
|
||||
private httpErrorHandlingService: HttpErrorHandlingService,
|
||||
private userService: UserService,
|
||||
private evaluatorService: EvaluatorService,
|
||||
private logger: LoggingService,
|
||||
private sanitizer: DomSanitizer,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
@ -130,38 +140,38 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
|
|||
this.planService.getSingle(itemId, this.lookupFields())
|
||||
.pipe(takeUntil(this._destroyed))
|
||||
.subscribe({
|
||||
next: (data) => {
|
||||
this.breadcrumbService.addIdResolvedValue(data.id?.toString(), data.label);
|
||||
next: (data) => {
|
||||
this.breadcrumbService.addIdResolvedValue(data.id?.toString(), data.label);
|
||||
|
||||
this.plan = data;
|
||||
this.plan.planUsers = this.isActive ? data?.planUsers?.filter((x) => x.isActive === IsActive.Active) : data?.planUsers;
|
||||
this.plan.otherPlanVersions = data.otherPlanVersions?.filter(x => x.isActive === IsActive.Active) || null;
|
||||
if (this.plan.descriptions && this.isActive) {
|
||||
if (this.plan.status?.internalStatus == PlanStatusEnum.Finalized) {
|
||||
this.plan.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status?.internalStatus === DescriptionStatusEnum.Finalized);
|
||||
} else {
|
||||
this.plan.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status?.internalStatus !== DescriptionStatusEnum.Canceled);
|
||||
}
|
||||
}
|
||||
if (data.entityDois && data.entityDois.length > 0) this.plan.entityDois = data.entityDois.filter(x => x.isActive === IsActive.Active);
|
||||
this.selectedBlueprint = data.blueprint;
|
||||
this.researchers = this.referenceService.getReferencesForTypes(this.plan?.planReferences, [this.referenceTypeService.getResearcherReferenceType()]);
|
||||
if (!this.hasDoi()) {
|
||||
this.selectedModel = this.plan.entityDois[0];
|
||||
}
|
||||
this.checkLockStatus(this.plan.id);
|
||||
},
|
||||
error: (error: any) => {
|
||||
this.httpErrorHandlingService.handleBackedRequestError(error);
|
||||
this.plan = data;
|
||||
this.plan.planUsers = this.isActive ? data?.planUsers?.filter((x) => x.isActive === IsActive.Active) : data?.planUsers;
|
||||
this.plan.otherPlanVersions = data.otherPlanVersions?.filter(x => x.isActive === IsActive.Active) || null;
|
||||
if (this.plan.descriptions && this.isActive) {
|
||||
if (this.plan.status?.internalStatus == PlanStatusEnum.Finalized) {
|
||||
this.plan.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status?.internalStatus === DescriptionStatusEnum.Finalized);
|
||||
} else {
|
||||
this.plan.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status?.internalStatus !== DescriptionStatusEnum.Canceled);
|
||||
}
|
||||
}
|
||||
if (data.entityDois && data.entityDois.length > 0) this.plan.entityDois = data.entityDois.filter(x => x.isActive === IsActive.Active);
|
||||
this.selectedBlueprint = data.blueprint;
|
||||
this.researchers = this.referenceService.getReferencesForTypes(this.plan?.planReferences, [this.referenceTypeService.getResearcherReferenceType()]);
|
||||
if (!this.hasDoi()) {
|
||||
this.selectedModel = this.plan.entityDois[0];
|
||||
}
|
||||
this.checkLockStatus(this.plan.id);
|
||||
},
|
||||
error: (error: any) => {
|
||||
this.httpErrorHandlingService.handleBackedRequestError(error);
|
||||
|
||||
if (error.status === 404) {
|
||||
return this.onFetchingDeletedCallbackError('/plans/');
|
||||
}
|
||||
if (error.status === 403) {
|
||||
return this.onFetchingForbiddenCallbackError('/plans/');
|
||||
}
|
||||
}
|
||||
})
|
||||
if (error.status === 404) {
|
||||
return this.onFetchingDeletedCallbackError('/plans/');
|
||||
}
|
||||
if (error.status === 403) {
|
||||
return this.onFetchingForbiddenCallbackError('/plans/');
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
else if (publicId != null) {
|
||||
this.isNew = false;
|
||||
|
@ -170,27 +180,27 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
|
|||
this.planService.getPublicSingle(publicId, this.lookupFields())
|
||||
.pipe(takeUntil(this._destroyed))
|
||||
.subscribe({
|
||||
next: (data) => {
|
||||
this.breadcrumbService.addExcludedParam('public', true);
|
||||
this.breadcrumbService.addIdResolvedValue(data.id?.toString(), data.label);
|
||||
next: (data) => {
|
||||
this.breadcrumbService.addExcludedParam('public', true);
|
||||
this.breadcrumbService.addIdResolvedValue(data.id?.toString(), data.label);
|
||||
|
||||
this.plan = data;
|
||||
this.researchers = this.referenceService.getReferencesForTypes(this.plan.planReferences, [this.referenceTypeService.getResearcherReferenceType()]); //data.planReferences is of wrong type!
|
||||
if (!this.hasDoi()) {
|
||||
this.selectedModel = this.plan.entityDois[0];
|
||||
}
|
||||
},
|
||||
error: (error: any) => {
|
||||
this.httpErrorHandlingService.handleBackedRequestError(error);
|
||||
this.plan = data;
|
||||
this.researchers = this.referenceService.getReferencesForTypes(this.plan.planReferences, [this.referenceTypeService.getResearcherReferenceType()]); //data.planReferences is of wrong type!
|
||||
if (!this.hasDoi()) {
|
||||
this.selectedModel = this.plan.entityDois[0];
|
||||
}
|
||||
},
|
||||
error: (error: any) => {
|
||||
this.httpErrorHandlingService.handleBackedRequestError(error);
|
||||
|
||||
if (error.status === 404) {
|
||||
return this.onFetchingDeletedCallbackError('/explore-plans');
|
||||
}
|
||||
if (error.status === 403) {
|
||||
return this.onFetchingForbiddenCallbackError('/explore-plans');
|
||||
}
|
||||
}
|
||||
});
|
||||
if (error.status === 404) {
|
||||
return this.onFetchingDeletedCallbackError('/explore-plans');
|
||||
}
|
||||
if (error.status === 403) {
|
||||
return this.onFetchingForbiddenCallbackError('/explore-plans');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
if (this.isAuthenticated()) {
|
||||
|
@ -205,9 +215,9 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
|
|||
])
|
||||
.pipe(takeUntil(this._destroyed))
|
||||
.subscribe({
|
||||
next: (repos) => this.depositRepos = repos,
|
||||
error: () => this.depositRepos = []
|
||||
})
|
||||
next: (repos) => this.depositRepos = repos,
|
||||
error: () => this.depositRepos = []
|
||||
})
|
||||
|
||||
this.userService.getSingle(this.authentication.userId(), [
|
||||
nameof<User>(x => x.id),
|
||||
|
@ -218,25 +228,25 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
|
|||
}
|
||||
}
|
||||
|
||||
get isActive(): boolean {
|
||||
return this.plan?.isActive != IsActive.Inactive;
|
||||
}
|
||||
get isActive(): boolean {
|
||||
return this.plan?.isActive != IsActive.Inactive;
|
||||
}
|
||||
|
||||
get selectedPlanVersion(): number {
|
||||
return this.plan?.version;
|
||||
}
|
||||
get selectedPlanVersion(): number {
|
||||
return this.plan?.version;
|
||||
}
|
||||
|
||||
get otherPlanVersions(): Plan[] | PublicPlan[]{
|
||||
return this.plan?.otherPlanVersions;
|
||||
}
|
||||
get otherPlanVersions(): Plan[] | PublicPlan[] {
|
||||
return this.plan?.otherPlanVersions;
|
||||
}
|
||||
|
||||
get unauthorizedTootipText(): string {
|
||||
return this.language.instant('PLAN-OVERVIEW.INFOS.UNAUTHORIZED-ORCID');
|
||||
}
|
||||
|
||||
get canEditStatus(): boolean{
|
||||
return (this.plan as Plan).statusAuthorizationFlags ?.some(x => x.toLowerCase() === PlanStatusPermission.Edit.toLowerCase())
|
||||
}
|
||||
get canEditStatus(): boolean {
|
||||
return (this.plan as Plan).statusAuthorizationFlags?.some(x => x.toLowerCase() === PlanStatusPermission.Edit.toLowerCase())
|
||||
}
|
||||
|
||||
onFetchingDeletedCallbackError(redirectRoot: string) {
|
||||
this.router.navigate([this.routerUtils.generateUrl(redirectRoot)]);
|
||||
|
@ -266,65 +276,119 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
|
|||
}
|
||||
|
||||
canEditPlan(): boolean {
|
||||
const authorizationFlags = !this.isPublicView ? (this.plan as Plan).authorizationFlags : [];
|
||||
const authorizationFlags = !this.isPublicView ? (this.plan as Plan).authorizationFlags : [];
|
||||
return (this.isDraftPlan()) && (authorizationFlags?.some(x => x === AppPermission.EditPlan) || this.authentication.hasPermission(AppPermission.EditPlan)) && this.isPublicView == false && this.plan.belongsToCurrentTenant != false;
|
||||
}
|
||||
|
||||
canCreateNewVersion(): boolean {
|
||||
const authorizationFlags = !this.isPublicView ? (this.plan as Plan).authorizationFlags : [];
|
||||
const versionStatus = !this.isPublicView ? (this.plan as Plan)?.versionStatus : null;
|
||||
const authorizationFlags = !this.isPublicView ? (this.plan as Plan).authorizationFlags : [];
|
||||
const versionStatus = !this.isPublicView ? (this.plan as Plan)?.versionStatus : null;
|
||||
return (authorizationFlags?.some(x => x === AppPermission.CreateNewVersionPlan) || this.authentication.hasPermission(AppPermission.CreateNewVersionPlan)) && versionStatus === PlanVersionStatus.Current && this.isPublicView == false && this.plan.belongsToCurrentTenant != false;
|
||||
}
|
||||
|
||||
canDeletePlan(): boolean {
|
||||
const authorizationFlags = !this.isPublicView ? (this.plan as Plan).authorizationFlags : [];
|
||||
const authorizationFlags = !this.isPublicView ? (this.plan as Plan).authorizationFlags : [];
|
||||
return (
|
||||
this.isActive &&
|
||||
(authorizationFlags?.some(x => x === AppPermission.DeletePlan) || this.authentication.hasPermission(AppPermission.DeletePlan)) &&
|
||||
this.isPublicView == false && this.plan.belongsToCurrentTenant != false
|
||||
)
|
||||
this.isActive &&
|
||||
(authorizationFlags?.some(x => x === AppPermission.DeletePlan) || this.authentication.hasPermission(AppPermission.DeletePlan)) &&
|
||||
this.isPublicView == false && this.plan.belongsToCurrentTenant != false
|
||||
)
|
||||
}
|
||||
|
||||
canClonePlan(): boolean {
|
||||
const authorizationFlags = !this.isPublicView ? (this.plan as Plan).authorizationFlags : [];
|
||||
const authorizationFlags = !this.isPublicView ? (this.plan as Plan).authorizationFlags : [];
|
||||
return (
|
||||
authorizationFlags?.some(x => x === AppPermission.ClonePlan) ||
|
||||
this.authentication.hasPermission(AppPermission.ClonePlan) ||
|
||||
(this.authentication.hasPermission(AppPermission.PublicClonePlan) && this.isPublicView)
|
||||
);
|
||||
authorizationFlags?.some(x => x === AppPermission.ClonePlan) ||
|
||||
this.authentication.hasPermission(AppPermission.ClonePlan) ||
|
||||
(this.authentication.hasPermission(AppPermission.PublicClonePlan) && this.isPublicView)
|
||||
);
|
||||
}
|
||||
|
||||
canFinalizePlan(): boolean {
|
||||
const authorizationFlags = !this.isPublicView ? (this.plan as Plan).authorizationFlags : [];
|
||||
const authorizationFlags = !this.isPublicView ? (this.plan as Plan).authorizationFlags : [];
|
||||
return (
|
||||
this.isActive &&
|
||||
(authorizationFlags?.some(x => x === AppPermission.FinalizePlan) || this.authentication.hasPermission(AppPermission.FinalizePlan))) &&
|
||||
this.isPublicView == false && this.plan.belongsToCurrentTenant != false;
|
||||
this.isActive &&
|
||||
(authorizationFlags?.some(x => x === AppPermission.FinalizePlan) || this.authentication.hasPermission(AppPermission.FinalizePlan))) &&
|
||||
this.isPublicView == false && this.plan.belongsToCurrentTenant != false;
|
||||
}
|
||||
|
||||
canExportPlan(): boolean {
|
||||
const authorizationFlags = !this.isPublicView ? (this.plan as Plan).authorizationFlags : [];
|
||||
const authorizationFlags = !this.isPublicView ? (this.plan as Plan).authorizationFlags : [];
|
||||
return (authorizationFlags?.some(x => x === AppPermission.ExportPlan) || this.authentication.hasPermission(AppPermission.ExportPlan)) &&
|
||||
this.plan?.status?.definition?.availableActions?.filter(x => x === PlanStatusAvailableActionType.Export).length > 0;
|
||||
this.plan?.status?.definition?.availableActions?.filter(x => x === PlanStatusAvailableActionType.Export).length > 0;
|
||||
|
||||
}
|
||||
|
||||
canEvaluatePlan(): boolean {
|
||||
const authorizationFlags = !this.isPublicView ? (this.plan as Plan).authorizationFlags : [];
|
||||
return (authorizationFlags?.some(x => x === AppPermission.EvaluatePlan) || this.authentication.hasPermission(AppPermission.EvaluatePlan));
|
||||
}
|
||||
|
||||
// onEvaluatePlan(planId: Guid, evaluatorId: string, format: string, isPublicView: boolean) {
|
||||
// this.evaluatorService.rankPlan(planId, evaluatorId, format).subscribe(
|
||||
// (response: RankModel) => {
|
||||
|
||||
// const dialogRef = this.dialog.open(PlanEvaluateDialogComponent, {
|
||||
// data: { rankData: response }
|
||||
// });
|
||||
|
||||
// dialogRef.afterClosed().subscribe(result => {
|
||||
// this.logger.debug("Dialog closed with result:", result);
|
||||
// });
|
||||
// },
|
||||
// error => {
|
||||
|
||||
// this.logger.error("Error ranking plan:", error);
|
||||
// }
|
||||
// );
|
||||
// }
|
||||
|
||||
onEvaluatePlan(planId: Guid, evaluatorId: string, format: string, isPublicView: boolean) {
|
||||
this.evaluatorService.rankPlan(planId, evaluatorId, format).subscribe(
|
||||
(response: RankModel) => {
|
||||
this.evaluatorService.getLogo(evaluatorId).subscribe(
|
||||
(logo: string) => {
|
||||
|
||||
this.logos.set(evaluatorId, this.sanitizer.bypassSecurityTrustResourceUrl('data:image/png;base64, ' + logo));
|
||||
|
||||
const dialogRef = this.dialog.open(PlanEvaluateDialogComponent, {
|
||||
data: {
|
||||
rankData: response,
|
||||
}
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
this.logger.debug("Dialog closed with result:", result);
|
||||
});
|
||||
},
|
||||
error => {
|
||||
this.logger.error("Error fetching evaluator logo:", error);
|
||||
}
|
||||
);
|
||||
},
|
||||
error => {
|
||||
this.logger.error("Error ranking plan:", error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
canInvitePlanUsers(): boolean {
|
||||
const authorizationFlags = !this.isPublicView ? (this.plan as Plan).authorizationFlags : [];
|
||||
const authorizationFlags = !this.isPublicView ? (this.plan as Plan).authorizationFlags : [];
|
||||
return (
|
||||
this.isActive &&
|
||||
(authorizationFlags?.some(x => x === AppPermission.InvitePlanUsers) || this.authentication.hasPermission(AppPermission.InvitePlanUsers))) &&
|
||||
this.isPublicView == false && this.plan.belongsToCurrentTenant != false;
|
||||
this.isActive &&
|
||||
(authorizationFlags?.some(x => x === AppPermission.InvitePlanUsers) || this.authentication.hasPermission(AppPermission.InvitePlanUsers))) &&
|
||||
this.isPublicView == false && this.plan.belongsToCurrentTenant != false;
|
||||
}
|
||||
|
||||
canAssignPlanUsers(): boolean {
|
||||
const authorizationFlags = !this.isPublicView ? (this.plan as Plan).authorizationFlags : [];
|
||||
const authorizationFlags = !this.isPublicView ? (this.plan as Plan).authorizationFlags : [];
|
||||
return (authorizationFlags?.some(x => x === AppPermission.AssignPlanUsers) || this.authentication.hasPermission(AppPermission.AssignPlanUsers)) && this.isPublicView == false && this.plan.belongsToCurrentTenant != false &&
|
||||
(this.plan.status.internalStatus == null || this.plan.status.internalStatus != PlanStatusEnum.Finalized);
|
||||
(this.plan.status.internalStatus == null || this.plan.status.internalStatus != PlanStatusEnum.Finalized);
|
||||
}
|
||||
|
||||
canDepositPlan(): boolean {
|
||||
const authorizationFlags = !this.isPublicView ? (this.plan as Plan).authorizationFlags : [];
|
||||
const authorizationFlags = !this.isPublicView ? (this.plan as Plan).authorizationFlags : [];
|
||||
return (authorizationFlags?.some(x => x === AppPermission.DepositPlan) || this.authentication.hasPermission(AppPermission.DepositPlan)) && this.isPublicView == false && this.plan.belongsToCurrentTenant != false &&
|
||||
this.plan?.status?.definition?.availableActions?.filter(x => x === PlanStatusAvailableActionType.Deposit).length > 0;
|
||||
}
|
||||
|
@ -395,7 +459,7 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
|
|||
.subscribe({
|
||||
complete: () => this.onDeleteCallbackSuccess(),
|
||||
error: (error) => this.onDeleteCallbackError(error)
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -453,17 +517,17 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
|
|||
persistStatus(status: PlanStatus) {
|
||||
if (status.internalStatus != null && status.internalStatus === PlanStatusEnum.Finalized) {
|
||||
this.finalize(status.id);
|
||||
} else if (this.plan.status.internalStatus === PlanStatusEnum.Finalized){
|
||||
} else if (this.plan.status.internalStatus === PlanStatusEnum.Finalized) {
|
||||
this.reverseFinalization(status.id);
|
||||
} else {
|
||||
// other statuses
|
||||
this.planService.setStatus(this.plan.id, status.id).pipe(takeUntil(this._destroyed))
|
||||
.subscribe({
|
||||
complete: () => {this.reloadPage(); this.onUpdateCallbackSuccess()},
|
||||
error:(error: any) => {
|
||||
this.onUpdateCallbackError(error)
|
||||
}
|
||||
});
|
||||
.subscribe({
|
||||
complete: () => { this.reloadPage(); this.onUpdateCallbackSuccess() },
|
||||
error: (error: any) => {
|
||||
this.onUpdateCallbackError(error)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -482,14 +546,14 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
|
|||
this.planService.setStatus(this.plan.id, newStatusId, result.descriptionsToBeFinalized)
|
||||
.pipe(takeUntil(this._destroyed))
|
||||
.subscribe({
|
||||
complete: () => {
|
||||
this.reloadPage();
|
||||
this.onUpdateCallbackSuccess()
|
||||
},
|
||||
error: (error: any) => {
|
||||
this.onUpdateCallbackError(error)
|
||||
}
|
||||
});
|
||||
complete: () => {
|
||||
this.reloadPage();
|
||||
this.onUpdateCallbackSuccess()
|
||||
},
|
||||
error: (error: any) => {
|
||||
this.onUpdateCallbackError(error)
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -551,11 +615,11 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
|
|||
if (result) {
|
||||
this.planService.setStatus(this.plan.id, newStatusId).pipe(takeUntil(this._destroyed))
|
||||
.subscribe({
|
||||
complete: () => {this.reloadPage(); this.onUpdateCallbackSuccess()},
|
||||
error:(error: any) => {
|
||||
this.onUpdateCallbackError(error)
|
||||
}
|
||||
});
|
||||
complete: () => { this.reloadPage(); this.onUpdateCallbackSuccess() },
|
||||
error: (error: any) => {
|
||||
this.onUpdateCallbackError(error)
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -589,14 +653,14 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
|
|||
};
|
||||
this.planService.removeUser(planUserRemovePersist).pipe(takeUntil(this._destroyed))
|
||||
.subscribe({
|
||||
complete: () => {
|
||||
this.reloadPage();
|
||||
this.onUpdateCallbackSuccess()
|
||||
},
|
||||
error: (error: any) => {
|
||||
this.onUpdateCallbackError(error)
|
||||
}
|
||||
});
|
||||
complete: () => {
|
||||
this.reloadPage();
|
||||
this.onUpdateCallbackSuccess()
|
||||
},
|
||||
error: (error: any) => {
|
||||
this.onUpdateCallbackError(error)
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -638,27 +702,27 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
|
|||
checkLockStatus(id: Guid) {
|
||||
this.lockService.checkLockStatus(Guid.parse(id.toString())).pipe(takeUntil(this._destroyed))
|
||||
.subscribe({
|
||||
next: (lockStatus) => {
|
||||
this.isLocked = lockStatus.status;
|
||||
if (this.isLocked) {
|
||||
this.dialog.open(PopupNotificationDialogComponent, {
|
||||
data: {
|
||||
title: this.language.instant('PLAN-OVERVIEW.LOCKED-DIALOG.TITLE'),
|
||||
message: this.language.instant('PLAN-OVERVIEW.LOCKED-DIALOG.MESSAGE')
|
||||
}, maxWidth: '30em'
|
||||
});
|
||||
}
|
||||
},
|
||||
error: (error) => {
|
||||
next: (lockStatus) => {
|
||||
this.isLocked = lockStatus.status;
|
||||
if (this.isLocked) {
|
||||
this.dialog.open(PopupNotificationDialogComponent, {
|
||||
data: {
|
||||
title: this.language.instant('PLAN-OVERVIEW.LOCKED-DIALOG.TITLE'),
|
||||
message: this.language.instant('PLAN-OVERVIEW.LOCKED-DIALOG.MESSAGE')
|
||||
}, maxWidth: '30em'
|
||||
});
|
||||
}
|
||||
},
|
||||
error: (error) => {
|
||||
this.router.navigate([this.routerUtils.generateUrl('/plans')]);
|
||||
this.httpErrorHandlingService.handleBackedRequestError(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private lookupFields(): string[] {
|
||||
return [
|
||||
nameof<BasePlan>(x => x.isActive),
|
||||
nameof<BasePlan>(x => x.isActive),
|
||||
nameof<Plan>(x => x.id),
|
||||
nameof<Plan>(x => x.label),
|
||||
nameof<Plan>(x => x.description),
|
||||
|
@ -679,6 +743,7 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
|
|||
[nameof<Plan>(x => x.authorizationFlags), AppPermission.ClonePlan].join('.'),
|
||||
[nameof<Plan>(x => x.authorizationFlags), AppPermission.FinalizePlan].join('.'),
|
||||
[nameof<Plan>(x => x.authorizationFlags), AppPermission.ExportPlan].join('.'),
|
||||
[nameof<Plan>(x => x.authorizationFlags), AppPermission.EvaluatePlan].join('.'),
|
||||
[nameof<Plan>(x => x.authorizationFlags), AppPermission.InvitePlanUsers].join('.'),
|
||||
[nameof<Plan>(x => x.authorizationFlags), AppPermission.AssignPlanUsers].join('.'),
|
||||
[nameof<Plan>(x => x.authorizationFlags), AppPermission.EditPlan].join('.'),
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
<h1 mat-dialog-title>{{ 'PLAN-EVALUATE-DIALOG.HEADER' | translate }}</h1>
|
||||
<div mat-dialog-content>
|
||||
<mat-card>
|
||||
<mat-card-header>
|
||||
<mat-card-title>{{'PLAN-EVALUATE-DIALOG.DETAILS-SUB-HEADER' | translate}}</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<div *ngIf="data.rankData" class="dialog-content">
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>{{'PLAN-EVALUATE-DIALOG.RANK-BODY' | translate}}</mat-label>
|
||||
<input matInput [value]="data.rankData.body.rank" disabled>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>{{'PLAN-EVALUATE-DIALOG.DETAILS-BODY' | translate}}</mat-label>
|
||||
<textarea matInput [value]="data.rankData.body.details" rows="4" disabled></textarea>
|
||||
</mat-form-field>
|
||||
|
||||
<div *ngIf="data.rankData.body.messages">
|
||||
<mat-card-header>
|
||||
<mat-card-title>{{'PLAN-EVALUATE-DIALOG.MESSAGES-BODY' | translate}}</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card>
|
||||
<mat-card-content>
|
||||
<ul>
|
||||
<li *ngFor="let entry of data.rankData.body.messages | keyvalue">
|
||||
<strong>{{ entry.key }}:</strong> {{ entry.value }}
|
||||
</li>
|
||||
</ul>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
|
@ -0,0 +1,8 @@
|
|||
.dialog-content {
|
||||
display: flex;
|
||||
flex-direction: column; /* Stack form fields vertically */
|
||||
}
|
||||
|
||||
mat-form-field {
|
||||
margin-bottom: 16px; /* Space between fields */
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PlanEvaluateDialogComponent } from './plan-evaluate-dialog.component';
|
||||
|
||||
describe('PlanEvaluateDialogComponent', () => {
|
||||
let component: PlanEvaluateDialogComponent;
|
||||
let fixture: ComponentFixture<PlanEvaluateDialogComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [PlanEvaluateDialogComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(PlanEvaluateDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
import { Component, Inject } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { RankModel } from '@app/core/model/evaluator/evaluator-plan-model.model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-plan-evaluate-dialog',
|
||||
templateUrl: './plan-evaluate-dialog.component.html',
|
||||
styleUrl: './plan-evaluate-dialog.component.scss'
|
||||
})
|
||||
export class PlanEvaluateDialogComponent {
|
||||
// Injecting the dialog data into the component
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<PlanEvaluateDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: { rankData: RankModel },
|
||||
) { }
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { NgIf } from '@angular/common';
|
||||
import { PlanEvaluateDialogComponent } from './plan-evaluate-dialog.component';
|
||||
import { CommonUiModule } from '@common/ui/common-ui.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatCardModule,
|
||||
MatButtonModule,
|
||||
MatInputModule,
|
||||
MatFormFieldModule,
|
||||
NgIf,
|
||||
CommonUiModule,
|
||||
],
|
||||
declarations: [PlanEvaluateDialogComponent],
|
||||
exports: [PlanEvaluateDialogComponent]
|
||||
})
|
||||
export class EvaluatePlanDialogModule { }
|
|
@ -4,6 +4,7 @@ import { PlanRoutingModule, PublicPlanRoutingModule } from '@app/ui/plan/plan.ro
|
|||
import { CommonFormsModule } from '@common/forms/common-forms.module';
|
||||
import { CommonUiModule } from '@common/ui/common-ui.module';
|
||||
import { InvitationAcceptedComponent } from './invitation/accepted/plan-invitation-accepted.component';
|
||||
import { EvaluatePlanDialogModule } from './plan-evaluate-dialog/plan-evaluate-dialog.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -11,6 +12,7 @@ import { InvitationAcceptedComponent } from './invitation/accepted/plan-invitati
|
|||
CommonFormsModule,
|
||||
FormattingModule,
|
||||
PlanRoutingModule,
|
||||
EvaluatePlanDialogModule,
|
||||
],
|
||||
declarations: [
|
||||
InvitationAcceptedComponent
|
||||
|
|
|
@ -190,6 +190,9 @@
|
|||
"JSON": "RDA JSON",
|
||||
"DOCX": "Document"
|
||||
},
|
||||
"EVALUATOR": {
|
||||
"FAIR": "FAIR Evaluator"
|
||||
},
|
||||
"LANGUAGES": {
|
||||
"en": "English",
|
||||
"gr": "Greek",
|
||||
|
@ -856,7 +859,8 @@
|
|||
"NEW-VERSION": "Start new version",
|
||||
"INVITE-SHORT": "Invite",
|
||||
"REMOVE-AUTHOR": "Remove",
|
||||
"PREVIEW": "Preview"
|
||||
"PREVIEW": "Preview",
|
||||
"EVALUATE": "Evaluate"
|
||||
},
|
||||
"UNDO-FINALIZATION-DIALOG": {
|
||||
"TITLE": "Undo Finalization?",
|
||||
|
@ -1002,7 +1006,8 @@
|
|||
"EXPORT": "Export",
|
||||
"INVITE-SHORT": "Invite",
|
||||
"REMOVE-AUTHOR": "Remove",
|
||||
"PREVIEW": "Preview"
|
||||
"PREVIEW": "Preview",
|
||||
"EVALUATE": "Evaluate"
|
||||
},
|
||||
"COPY-DIALOG": {
|
||||
"COPY": "Copy",
|
||||
|
@ -1941,7 +1946,9 @@
|
|||
"SNACK-BAR": {
|
||||
"UNSUCCESSFUL-DOI": "DOIa ez da behar bezala sortu",
|
||||
"SUCCESSFUL-DOI": "DOIa ondo sortu da",
|
||||
"SUCCESSFUL-PLAN-CONTACT": "User added"
|
||||
"SUCCESSFUL-PLAN-CONTACT": "User added",
|
||||
"SUCCESSFUL-EVALUATION": "Successful evaluation",
|
||||
"UNSUCCESSFUL-EVALUATION":"Unsuccessful evaluation"
|
||||
},
|
||||
"DESCRIPTION-TEMPLATE-LIST": {
|
||||
"TITLE": "Available Description Templates",
|
||||
|
@ -1959,6 +1966,20 @@
|
|||
"MESSAGE": "Somebody else is modifying the Plan at this moment. You may view the Plan but you cannot make any changes."
|
||||
}
|
||||
},
|
||||
"PLAN-EVALUATE-DIALOG":{
|
||||
"HEADER": "Ranking Information",
|
||||
"DETAILS-SUB-HEADER": "Details",
|
||||
"RANK-BODY": "Rank",
|
||||
"DETAILS-BODY": "Details",
|
||||
"MESSAGES-BODY": "Messages"
|
||||
},
|
||||
"DESCRIPTION-EVALUATE-DIALOG":{
|
||||
"HEADER": "Ranking Information",
|
||||
"DETAILS-SUB-HEADER": "Details",
|
||||
"RANK-BODY": "Rank",
|
||||
"DETAILS-BODY": "Details",
|
||||
"MESSAGES-BODY": "Messages"
|
||||
},
|
||||
"REFERENCE-FIELD": {
|
||||
"COULD-NOT-FIND-MESSAGE": "Couldn't find it?",
|
||||
"ACTIONS": {
|
||||
|
|
|
@ -193,6 +193,9 @@
|
|||
"JSON": "RDA JSON",
|
||||
"DOCX": "Document"
|
||||
},
|
||||
"EVALUATOR": {
|
||||
"FAIR": "FAIR Evaluator"
|
||||
},
|
||||
"LANGUAGES": {
|
||||
"en": "English",
|
||||
"gr": "Greek",
|
||||
|
@ -859,7 +862,8 @@
|
|||
"NEW-VERSION": "Start new version",
|
||||
"INVITE-SHORT": "Invite",
|
||||
"REMOVE-AUTHOR": "Remove",
|
||||
"PREVIEW": "Preview"
|
||||
"PREVIEW": "Preview",
|
||||
"EVALUATE": "Evaluate"
|
||||
},
|
||||
"UNDO-FINALIZATION-DIALOG": {
|
||||
"TITLE": "Undo Finalization?",
|
||||
|
@ -1005,7 +1009,8 @@
|
|||
"EXPORT": "Export",
|
||||
"INVITE-SHORT": "Invite",
|
||||
"REMOVE-AUTHOR": "Remove",
|
||||
"PREVIEW": "Preview"
|
||||
"PREVIEW": "Preview",
|
||||
"EVALUATE": "Evaluate"
|
||||
},
|
||||
"COPY-DIALOG": {
|
||||
"COPY": "Copy",
|
||||
|
@ -1944,7 +1949,9 @@
|
|||
"SNACK-BAR": {
|
||||
"UNSUCCESSFUL-DOI": "DOI Erstellung fehlgeschlagen",
|
||||
"SUCCESSFUL-DOI": "DOI Erstellung erfolgreich",
|
||||
"SUCCESSFUL-PLAN-CONTACT": "User added"
|
||||
"SUCCESSFUL-PLAN-CONTACT": "User added",
|
||||
"SUCCESSFUL-EVALUATION": "Successful evaluation",
|
||||
"UNSUCCESSFUL-EVALUATION":"Unsuccessful evaluation"
|
||||
},
|
||||
"DESCRIPTION-TEMPLATE-LIST": {
|
||||
"TITLE": "Available Description Templates",
|
||||
|
@ -1962,6 +1969,20 @@
|
|||
"MESSAGE": "Somebody else is modifying the Plan at this moment. You may view the Plan but you cannot make any changes."
|
||||
}
|
||||
},
|
||||
"PLAN-EVALUATE-DIALOG":{
|
||||
"HEADER": "Ranking Information",
|
||||
"DETAILS-SUB-HEADER": "Details",
|
||||
"RANK-BODY": "Rank",
|
||||
"DETAILS-BODY": "Details",
|
||||
"MESSAGES-BODY": "Messages"
|
||||
},
|
||||
"DESCRIPTION-EVALUATE-DIALOG":{
|
||||
"HEADER": "Ranking Information",
|
||||
"DETAILS-SUB-HEADER": "Details",
|
||||
"RANK-BODY": "Rank",
|
||||
"DETAILS-BODY": "Details",
|
||||
"MESSAGES-BODY": "Messages"
|
||||
},
|
||||
"REFERENCE-FIELD": {
|
||||
"COULD-NOT-FIND-MESSAGE": "Couldn't find it?",
|
||||
"ACTIONS": {
|
||||
|
|
|
@ -193,6 +193,9 @@
|
|||
"JSON": "RDA JSON",
|
||||
"DOCX": "Document"
|
||||
},
|
||||
"EVALUATOR": {
|
||||
"FAIR": "FAIR Evaluator"
|
||||
},
|
||||
"LANGUAGES": {
|
||||
"en": "English",
|
||||
"gr": "Greek",
|
||||
|
@ -857,7 +860,8 @@
|
|||
"NEW-VERSION": "Start new version",
|
||||
"INVITE-SHORT": "Invite",
|
||||
"REMOVE-AUTHOR": "Remove",
|
||||
"PREVIEW": "Preview"
|
||||
"PREVIEW": "Preview",
|
||||
"EVALUATE": "Evaluate"
|
||||
},
|
||||
"UNDO-FINALIZATION-DIALOG": {
|
||||
"TITLE": "Undo Finalization?",
|
||||
|
@ -1003,7 +1007,8 @@
|
|||
"EXPORT": "Export",
|
||||
"INVITE-SHORT": "Invite",
|
||||
"REMOVE-AUTHOR": "Remove",
|
||||
"PREVIEW": "Preview"
|
||||
"PREVIEW": "Preview",
|
||||
"EVALUATE": "Evaluate"
|
||||
},
|
||||
"COPY-DIALOG": {
|
||||
"COPY": "Copy",
|
||||
|
@ -1944,7 +1949,9 @@
|
|||
"SNACK-BAR": {
|
||||
"UNSUCCESSFUL-DOI": "Unsuccessful DOI creation",
|
||||
"SUCCESSFUL-DOI": "Successful DOI creation",
|
||||
"SUCCESSFUL-PLAN-CONTACT": "User added"
|
||||
"SUCCESSFUL-PLAN-CONTACT": "User added",
|
||||
"SUCCESSFUL-EVALUATION": "Successful evaluation",
|
||||
"UNSUCCESSFUL-EVALUATION": "Unsuccessful evaluation"
|
||||
},
|
||||
"DESCRIPTION-TEMPLATE-LIST": {
|
||||
"TITLE": "Available Description Templates",
|
||||
|
@ -1962,6 +1969,20 @@
|
|||
"MESSAGE": "Somebody else is modifying the Plan at this moment. You may view the Plan but you cannot make any changes."
|
||||
}
|
||||
},
|
||||
"PLAN-EVALUATE-DIALOG":{
|
||||
"HEADER": "Ranking Information",
|
||||
"DETAILS-SUB-HEADER": "Details",
|
||||
"RANK-BODY": "Rank",
|
||||
"DETAILS-BODY": "Details",
|
||||
"MESSAGES-BODY": "Messages"
|
||||
},
|
||||
"DESCRIPTION-EVALUATE-DIALOG":{
|
||||
"HEADER": "Ranking Information",
|
||||
"DETAILS-SUB-HEADER": "Details",
|
||||
"RANK-BODY": "Rank",
|
||||
"DETAILS-BODY": "Details",
|
||||
"MESSAGES-BODY": "Messages"
|
||||
},
|
||||
"REFERENCE-FIELD": {
|
||||
"COULD-NOT-FIND-MESSAGE": "Couldn't find it?",
|
||||
"ACTIONS": {
|
||||
|
|
|
@ -193,6 +193,9 @@
|
|||
"JSON": "RDA JSON",
|
||||
"DOCX": "Document"
|
||||
},
|
||||
"EVALUATOR": {
|
||||
"FAIR": "FAIR Evaluator"
|
||||
},
|
||||
"LANGUAGES": {
|
||||
"en": "English",
|
||||
"gr": "Greek",
|
||||
|
@ -859,7 +862,8 @@
|
|||
"NEW-VERSION": "Start new version",
|
||||
"INVITE-SHORT": "Invite",
|
||||
"REMOVE-AUTHOR": "Remove",
|
||||
"PREVIEW": "Preview"
|
||||
"PREVIEW": "Preview",
|
||||
"EVALUATE": "Evaluate"
|
||||
},
|
||||
"UNDO-FINALIZATION-DIALOG": {
|
||||
"TITLE": "Undo Finalization?",
|
||||
|
@ -1005,7 +1009,8 @@
|
|||
"EXPORT": "Export",
|
||||
"INVITE-SHORT": "Invite",
|
||||
"REMOVE-AUTHOR": "Remove",
|
||||
"PREVIEW": "Preview"
|
||||
"PREVIEW": "Preview",
|
||||
"EVALUATE": "Evaluate"
|
||||
},
|
||||
"COPY-DIALOG": {
|
||||
"COPY": "Copy",
|
||||
|
@ -1944,7 +1949,9 @@
|
|||
"SNACK-BAR": {
|
||||
"UNSUCCESSFUL-DOI": "Fallo en la creación del DOI",
|
||||
"SUCCESSFUL-DOI": "Creación del DOI correcta",
|
||||
"SUCCESSFUL-PLAN-CONTACT": "User added"
|
||||
"SUCCESSFUL-PLAN-CONTACT": "User added",
|
||||
"SUCCESSFUL-EVALUATION": "Successful evaluation",
|
||||
"UNSUCCESSFUL-EVALUATION":"Unsuccessful evaluation"
|
||||
},
|
||||
"DESCRIPTION-TEMPLATE-LIST": {
|
||||
"TITLE": "Available Description Templates",
|
||||
|
@ -1962,6 +1969,20 @@
|
|||
"MESSAGE": "Somebody else is modifying the Plan at this moment. You may view the Plan but you cannot make any changes."
|
||||
}
|
||||
},
|
||||
"PLAN-EVALUATE-DIALOG": {
|
||||
"HEADER": "Ranking Information",
|
||||
"DETAILS-SUB-HEADER": "Details",
|
||||
"RANK-BODY": "Rank",
|
||||
"DETAILS-BODY": "Details",
|
||||
"MESSAGES-BODY": "Messages"
|
||||
},
|
||||
"DESCRIPTION-EVALUATE-DIALOG": {
|
||||
"HEADER": "Ranking Information",
|
||||
"DETAILS-SUB-HEADER": "Details",
|
||||
"RANK-BODY": "Rank",
|
||||
"DETAILS-BODY": "Details",
|
||||
"MESSAGES-BODY": "Messages"
|
||||
},
|
||||
"REFERENCE-FIELD": {
|
||||
"COULD-NOT-FIND-MESSAGE": "Couldn't find it?",
|
||||
"ACTIONS": {
|
||||
|
|
|
@ -193,6 +193,9 @@
|
|||
"JSON": "RDA JSON",
|
||||
"DOCX": "Document"
|
||||
},
|
||||
"EVALUATOR": {
|
||||
"FAIR": "FAIR Evaluator"
|
||||
},
|
||||
"LANGUAGES": {
|
||||
"en": "English",
|
||||
"gr": "Greek",
|
||||
|
@ -859,7 +862,8 @@
|
|||
"NEW-VERSION": "Start new version",
|
||||
"INVITE-SHORT": "Invite",
|
||||
"REMOVE-AUTHOR": "Remove",
|
||||
"PREVIEW": "Preview"
|
||||
"PREVIEW": "Preview",
|
||||
"EVALUATE": "Evaluate"
|
||||
},
|
||||
"UNDO-FINALIZATION-DIALOG": {
|
||||
"TITLE": "Undo Finalization?",
|
||||
|
@ -1005,7 +1009,8 @@
|
|||
"EXPORT": "Export",
|
||||
"INVITE-SHORT": "Invite",
|
||||
"REMOVE-AUTHOR": "Remove",
|
||||
"PREVIEW": "Preview"
|
||||
"PREVIEW": "Preview",
|
||||
"EVALUATE": "Evaluate"
|
||||
},
|
||||
"COPY-DIALOG": {
|
||||
"COPY": "Copy",
|
||||
|
@ -1944,7 +1949,9 @@
|
|||
"SNACK-BAR": {
|
||||
"UNSUCCESSFUL-DOI": "Αποτυχία δημιουργίας Μονοσήμαντων Αναγνωριστικών Ψηφιακών Αντικειμένων (DOI)",
|
||||
"SUCCESSFUL-DOI": "Επιτυχία δημιουργίας Μονοσήμαντων Αναγνωριστικών Ψηφιακών Αντικειμένων (DOI)",
|
||||
"SUCCESSFUL-PLAN-CONTACT": "User added"
|
||||
"SUCCESSFUL-PLAN-CONTACT": "User added",
|
||||
"SUCCESSFUL-EVALUATION": "Successful evaluation",
|
||||
"UNSUCCESSFUL-EVALUATION":"Unsuccessful evaluation"
|
||||
},
|
||||
"DESCRIPTION-TEMPLATE-LIST": {
|
||||
"TITLE": "Available Description Templates",
|
||||
|
@ -1962,6 +1969,20 @@
|
|||
"MESSAGE": "Somebody else is modifying the Plan at this moment. You may view the Plan but you cannot make any changes."
|
||||
}
|
||||
},
|
||||
"PLAN-EVALUATE-DIALOG": {
|
||||
"HEADER": "Ranking Information",
|
||||
"DETAILS-SUB-HEADER": "Details",
|
||||
"RANK-BODY": "Rank",
|
||||
"DETAILS-BODY": "Details",
|
||||
"MESSAGES-BODY": "Messages"
|
||||
},
|
||||
"DESCRIPTION-EVALUATE-DIALOG": {
|
||||
"HEADER": "Ranking Information",
|
||||
"DETAILS-SUB-HEADER": "Details",
|
||||
"RANK-BODY": "Rank",
|
||||
"DETAILS-BODY": "Details",
|
||||
"MESSAGES-BODY": "Messages"
|
||||
},
|
||||
"REFERENCE-FIELD": {
|
||||
"COULD-NOT-FIND-MESSAGE": "Couldn't find it?",
|
||||
"ACTIONS": {
|
||||
|
|
|
@ -193,6 +193,9 @@
|
|||
"JSON": "RDA JSON",
|
||||
"DOCX": "Document"
|
||||
},
|
||||
"EVALUATOR": {
|
||||
"FAIR": "FAIR Evaluator"
|
||||
},
|
||||
"LANGUAGES": {
|
||||
"en": "English",
|
||||
"gr": "Greek",
|
||||
|
@ -859,7 +862,8 @@
|
|||
"NEW-VERSION": "Start new version",
|
||||
"INVITE-SHORT": "Invite",
|
||||
"REMOVE-AUTHOR": "Remove",
|
||||
"PREVIEW": "Preview"
|
||||
"PREVIEW": "Preview",
|
||||
"EVALUATE": "Evaluate"
|
||||
},
|
||||
"UNDO-FINALIZATION-DIALOG": {
|
||||
"TITLE": "Undo Finalization?",
|
||||
|
@ -1005,7 +1009,8 @@
|
|||
"EXPORT": "Export",
|
||||
"INVITE-SHORT": "Invite",
|
||||
"REMOVE-AUTHOR": "Remove",
|
||||
"PREVIEW": "Preview"
|
||||
"PREVIEW": "Preview",
|
||||
"EVALUATE": "Evaluate"
|
||||
},
|
||||
"COPY-DIALOG": {
|
||||
"COPY": "Copy",
|
||||
|
@ -1944,7 +1949,9 @@
|
|||
"SNACK-BAR": {
|
||||
"UNSUCCESSFUL-DOI": "Neuspješno generiran DOI",
|
||||
"SUCCESSFUL-DOI": "Uspješno generiran DOI",
|
||||
"SUCCESSFUL-PLAN-CONTACT": "User added"
|
||||
"SUCCESSFUL-PLAN-CONTACT": "User added",
|
||||
"SUCCESSFUL-EVALUATION": "Successful evaluation",
|
||||
"UNSUCCESSFUL-EVALUATION":"Unsuccessful evaluation"
|
||||
},
|
||||
"DESCRIPTION-TEMPLATE-LIST": {
|
||||
"TITLE": "Available Description Templates",
|
||||
|
@ -1962,6 +1969,20 @@
|
|||
"MESSAGE": "Somebody else is modifying the Plan at this moment. You may view the Plan but you cannot make any changes."
|
||||
}
|
||||
},
|
||||
"PLAN-EVALUATE-DIALOG": {
|
||||
"HEADER": "Ranking Information",
|
||||
"DETAILS-SUB-HEADER": "Details",
|
||||
"RANK-BODY": "Rank",
|
||||
"DETAILS-BODY": "Details",
|
||||
"MESSAGES-BODY": "Messages"
|
||||
},
|
||||
"DESCRIPTION-EVALUATE-DIALOG": {
|
||||
"HEADER": "Ranking Information",
|
||||
"DETAILS-SUB-HEADER": "Details",
|
||||
"RANK-BODY": "Rank",
|
||||
"DETAILS-BODY": "Details",
|
||||
"MESSAGES-BODY": "Messages"
|
||||
},
|
||||
"REFERENCE-FIELD": {
|
||||
"COULD-NOT-FIND-MESSAGE": "Couldn't find it?",
|
||||
"ACTIONS": {
|
||||
|
|
|
@ -193,6 +193,9 @@
|
|||
"JSON": "RDA JSON",
|
||||
"DOCX": "Document"
|
||||
},
|
||||
"EVALUATOR": {
|
||||
"FAIR": "FAIR Evaluator"
|
||||
},
|
||||
"LANGUAGES": {
|
||||
"en": "English",
|
||||
"gr": "Greek",
|
||||
|
@ -859,7 +862,8 @@
|
|||
"NEW-VERSION": "Start new version",
|
||||
"INVITE-SHORT": "Invite",
|
||||
"REMOVE-AUTHOR": "Remove",
|
||||
"PREVIEW": "Preview"
|
||||
"PREVIEW": "Preview",
|
||||
"EVALUATE": "Evaluate"
|
||||
},
|
||||
"UNDO-FINALIZATION-DIALOG": {
|
||||
"TITLE": "Undo Finalization?",
|
||||
|
@ -1005,7 +1009,8 @@
|
|||
"EXPORT": "Export",
|
||||
"INVITE-SHORT": "Invite",
|
||||
"REMOVE-AUTHOR": "Remove",
|
||||
"PREVIEW": "Preview"
|
||||
"PREVIEW": "Preview",
|
||||
"EVALUATE": "Evaluate"
|
||||
},
|
||||
"COPY-DIALOG": {
|
||||
"COPY": "Copy",
|
||||
|
@ -1944,7 +1949,9 @@
|
|||
"SNACK-BAR": {
|
||||
"UNSUCCESSFUL-DOI": "Nie udało się utworzyć DOI",
|
||||
"SUCCESSFUL-DOI": "Utworzono DOI",
|
||||
"SUCCESSFUL-PLAN-CONTACT": "User added"
|
||||
"SUCCESSFUL-PLAN-CONTACT": "User added",
|
||||
"SUCCESSFUL-EVALUATION": "Successful evaluation",
|
||||
"UNSUCCESSFUL-EVALUATION":"Unsuccessful evaluation"
|
||||
},
|
||||
"DESCRIPTION-TEMPLATE-LIST": {
|
||||
"TITLE": "Available Description Templates",
|
||||
|
@ -1962,6 +1969,20 @@
|
|||
"MESSAGE": "Somebody else is modifying the Plan at this moment. You may view the Plan but you cannot make any changes."
|
||||
}
|
||||
},
|
||||
"PLAN-EVALUATE-DIALOG": {
|
||||
"HEADER": "Ranking Information",
|
||||
"DETAILS-SUB-HEADER": "Details",
|
||||
"RANK-BODY": "Rank",
|
||||
"DETAILS-BODY": "Details",
|
||||
"MESSAGES-BODY": "Messages"
|
||||
},
|
||||
"DESCRIPTION-EVALUATE-DIALOG": {
|
||||
"HEADER": "Ranking Information",
|
||||
"DETAILS-SUB-HEADER": "Details",
|
||||
"RANK-BODY": "Rank",
|
||||
"DETAILS-BODY": "Details",
|
||||
"MESSAGES-BODY": "Messages"
|
||||
},
|
||||
"REFERENCE-FIELD": {
|
||||
"COULD-NOT-FIND-MESSAGE": "Couldn't find it?",
|
||||
"ACTIONS": {
|
||||
|
|
|
@ -193,6 +193,9 @@
|
|||
"JSON": "RDA JSON",
|
||||
"DOCX": "Document"
|
||||
},
|
||||
"EVALUATOR": {
|
||||
"FAIR": "FAIR Evaluator"
|
||||
},
|
||||
"LANGUAGES": {
|
||||
"en": "English",
|
||||
"gr": "Greek",
|
||||
|
@ -859,7 +862,8 @@
|
|||
"NEW-VERSION": "Start new version",
|
||||
"INVITE-SHORT": "Invite",
|
||||
"REMOVE-AUTHOR": "Remove",
|
||||
"PREVIEW": "Preview"
|
||||
"PREVIEW": "Preview",
|
||||
"EVALUATE": "Evaluate"
|
||||
},
|
||||
"UNDO-FINALIZATION-DIALOG": {
|
||||
"TITLE": "Undo Finalization?",
|
||||
|
@ -1005,7 +1009,8 @@
|
|||
"EXPORT": "Export",
|
||||
"INVITE-SHORT": "Invite",
|
||||
"REMOVE-AUTHOR": "Remove",
|
||||
"PREVIEW": "Preview"
|
||||
"PREVIEW": "Preview",
|
||||
"EVALUATE": "Evaluate"
|
||||
},
|
||||
"COPY-DIALOG": {
|
||||
"COPY": "Copy",
|
||||
|
@ -1944,7 +1949,9 @@
|
|||
"SNACK-BAR": {
|
||||
"UNSUCCESSFUL-DOI": "Criação de DOI sem sucesso",
|
||||
"SUCCESSFUL-DOI": "Criação de DOI com sucesso",
|
||||
"SUCCESSFUL-PLAN-CONTACT": "User added"
|
||||
"SUCCESSFUL-PLAN-CONTACT": "User added",
|
||||
"SUCCESSFUL-EVALUATION": "Successful evaluation",
|
||||
"UNSUCCESSFUL-EVALUATION":"Unsuccessful evaluation"
|
||||
},
|
||||
"DESCRIPTION-TEMPLATE-LIST": {
|
||||
"TITLE": "Available Description Templates",
|
||||
|
@ -1962,6 +1969,20 @@
|
|||
"MESSAGE": "Somebody else is modifying the Plan at this moment. You may view the Plan but you cannot make any changes."
|
||||
}
|
||||
},
|
||||
"PLAN-EVALUATE-DIALOG": {
|
||||
"HEADER": "Ranking Information",
|
||||
"DETAILS-SUB-HEADER": "Details",
|
||||
"RANK-BODY": "Rank",
|
||||
"DETAILS-BODY": "Details",
|
||||
"MESSAGES-BODY": "Messages"
|
||||
},
|
||||
"DESCRIPTION-EVALUATE-DIALOG": {
|
||||
"HEADER": "Ranking Information",
|
||||
"DETAILS-SUB-HEADER": "Details",
|
||||
"RANK-BODY": "Rank",
|
||||
"DETAILS-BODY": "Details",
|
||||
"MESSAGES-BODY": "Messages"
|
||||
},
|
||||
"REFERENCE-FIELD": {
|
||||
"COULD-NOT-FIND-MESSAGE": "Couldn't find it?",
|
||||
"ACTIONS": {
|
||||
|
|
|
@ -193,6 +193,9 @@
|
|||
"JSON": "RDA JSON",
|
||||
"DOCX": "Document"
|
||||
},
|
||||
"EVALUATOR": {
|
||||
"FAIR": "FAIR Evaluator"
|
||||
},
|
||||
"LANGUAGES": {
|
||||
"en": "English",
|
||||
"gr": "Greek",
|
||||
|
@ -859,7 +862,8 @@
|
|||
"NEW-VERSION": "Start new version",
|
||||
"INVITE-SHORT": "Invite",
|
||||
"REMOVE-AUTHOR": "Remove",
|
||||
"PREVIEW": "Preview"
|
||||
"PREVIEW": "Preview",
|
||||
"EVALUATE": "Evaluate"
|
||||
},
|
||||
"UNDO-FINALIZATION-DIALOG": {
|
||||
"TITLE": "Undo Finalization?",
|
||||
|
@ -1005,7 +1009,8 @@
|
|||
"EXPORT": "Export",
|
||||
"INVITE-SHORT": "Invite",
|
||||
"REMOVE-AUTHOR": "Remove",
|
||||
"PREVIEW": "Preview"
|
||||
"PREVIEW": "Preview",
|
||||
"EVALUATE": "Evaluate"
|
||||
},
|
||||
"COPY-DIALOG": {
|
||||
"COPY": "Copy",
|
||||
|
@ -1944,7 +1949,9 @@
|
|||
"SNACK-BAR": {
|
||||
"UNSUCCESSFUL-DOI": "Neúspešné vytvorenie DOI",
|
||||
"SUCCESSFUL-DOI": "Úspešné vytvorenie DOI",
|
||||
"SUCCESSFUL-PLAN-CONTACT": "User added"
|
||||
"SUCCESSFUL-PLAN-CONTACT": "User added",
|
||||
"SUCCESSFUL-EVALUATION": "Successful evaluation",
|
||||
"UNSUCCESSFUL-EVALUATION":"Unsuccessful evaluation"
|
||||
},
|
||||
"DESCRIPTION-TEMPLATE-LIST": {
|
||||
"TITLE": "Available Description Templates",
|
||||
|
@ -1962,6 +1969,20 @@
|
|||
"MESSAGE": "Somebody else is modifying the Plan at this moment. You may view the Plan but you cannot make any changes."
|
||||
}
|
||||
},
|
||||
"PLAN-EVALUATE-DIALOG": {
|
||||
"HEADER": "Ranking Information",
|
||||
"DETAILS-SUB-HEADER": "Details",
|
||||
"RANK-BODY": "Rank",
|
||||
"DETAILS-BODY": "Details",
|
||||
"MESSAGES-BODY": "Messages"
|
||||
},
|
||||
"DESCRIPTION-EVALUATE-DIALOG": {
|
||||
"HEADER": "Ranking Information",
|
||||
"DETAILS-SUB-HEADER": "Details",
|
||||
"RANK-BODY": "Rank",
|
||||
"DETAILS-BODY": "Details",
|
||||
"MESSAGES-BODY": "Messages"
|
||||
},
|
||||
"REFERENCE-FIELD": {
|
||||
"COULD-NOT-FIND-MESSAGE": "Couldn't find it?",
|
||||
"ACTIONS": {
|
||||
|
|
|
@ -193,6 +193,9 @@
|
|||
"JSON": "RDA JSON",
|
||||
"DOCX": "Document"
|
||||
},
|
||||
"EVALUATOR": {
|
||||
"FAIR": "FAIR Evaluator"
|
||||
},
|
||||
"LANGUAGES": {
|
||||
"en": "English",
|
||||
"gr": "Greek",
|
||||
|
@ -859,7 +862,8 @@
|
|||
"NEW-VERSION": "Start new version",
|
||||
"INVITE-SHORT": "Invite",
|
||||
"REMOVE-AUTHOR": "Remove",
|
||||
"PREVIEW": "Preview"
|
||||
"PREVIEW": "Preview",
|
||||
"EVALUATE": "Evaluate"
|
||||
},
|
||||
"UNDO-FINALIZATION-DIALOG": {
|
||||
"TITLE": "Undo Finalization?",
|
||||
|
@ -1005,7 +1009,8 @@
|
|||
"EXPORT": "Export",
|
||||
"INVITE-SHORT": "Invite",
|
||||
"REMOVE-AUTHOR": "Remove",
|
||||
"PREVIEW": "Preview"
|
||||
"PREVIEW": "Preview",
|
||||
"EVALUATE": "Evaluate"
|
||||
},
|
||||
"COPY-DIALOG": {
|
||||
"COPY": "Copy",
|
||||
|
@ -1944,7 +1949,9 @@
|
|||
"SNACK-BAR": {
|
||||
"UNSUCCESSFUL-DOI": "Neuspešno registrovan DOI",
|
||||
"SUCCESSFUL-DOI": "Uspešno registrovan DOI",
|
||||
"SUCCESSFUL-PLAN-CONTACT": "User added"
|
||||
"SUCCESSFUL-PLAN-CONTACT": "User added",
|
||||
"SUCCESSFUL-EVALUATION": "Successful evaluation",
|
||||
"UNSUCCESSFUL-EVALUATION":"Unsuccessful evaluation"
|
||||
},
|
||||
"DESCRIPTION-TEMPLATE-LIST": {
|
||||
"TITLE": "Available Description Templates",
|
||||
|
@ -1962,6 +1969,20 @@
|
|||
"MESSAGE": "Somebody else is modifying the Plan at this moment. You may view the Plan but you cannot make any changes."
|
||||
}
|
||||
},
|
||||
"PLAN-EVALUATE-DIALOG": {
|
||||
"HEADER": "Ranking Information",
|
||||
"DETAILS-SUB-HEADER": "Details",
|
||||
"RANK-BODY": "Rank",
|
||||
"DETAILS-BODY": "Details",
|
||||
"MESSAGES-BODY": "Messages"
|
||||
},
|
||||
"DESCRIPTION-EVALUATE-DIALOG": {
|
||||
"HEADER": "Ranking Information",
|
||||
"DETAILS-SUB-HEADER": "Details",
|
||||
"RANK-BODY": "Rank",
|
||||
"DETAILS-BODY": "Details",
|
||||
"MESSAGES-BODY": "Messages"
|
||||
},
|
||||
"REFERENCE-FIELD": {
|
||||
"COULD-NOT-FIND-MESSAGE": "Couldn't find it?",
|
||||
"ACTIONS": {
|
||||
|
|
|
@ -193,6 +193,9 @@
|
|||
"JSON": "RDA JSON",
|
||||
"DOCX": "Document"
|
||||
},
|
||||
"EVALUATOR": {
|
||||
"FAIR": "FAIR Evaluator"
|
||||
},
|
||||
"LANGUAGES": {
|
||||
"en": "English",
|
||||
"gr": "Greek",
|
||||
|
@ -859,7 +862,8 @@
|
|||
"NEW-VERSION": "Start new version",
|
||||
"INVITE-SHORT": "Invite",
|
||||
"REMOVE-AUTHOR": "Remove",
|
||||
"PREVIEW": "Preview"
|
||||
"PREVIEW": "Preview",
|
||||
"EVALUATE": "Evaluate"
|
||||
},
|
||||
"UNDO-FINALIZATION-DIALOG": {
|
||||
"TITLE": "Undo Finalization?",
|
||||
|
@ -1005,7 +1009,8 @@
|
|||
"EXPORT": "Export",
|
||||
"INVITE-SHORT": "Invite",
|
||||
"REMOVE-AUTHOR": "Remove",
|
||||
"PREVIEW": "Preview"
|
||||
"PREVIEW": "Preview",
|
||||
"EVALUATE": "Evaluate"
|
||||
},
|
||||
"COPY-DIALOG": {
|
||||
"COPY": "Copy",
|
||||
|
@ -1944,7 +1949,9 @@
|
|||
"SNACK-BAR": {
|
||||
"UNSUCCESSFUL-DOI": "Başarısız DOI oluşturma",
|
||||
"SUCCESSFUL-DOI": "Başarılı DOI oluşumu",
|
||||
"SUCCESSFUL-PLAN-CONTACT": "User added"
|
||||
"SUCCESSFUL-PLAN-CONTACT": "User added",
|
||||
"SUCCESSFUL-EVALUATION": "Successful evaluation",
|
||||
"UNSUCCESSFUL-EVALUATION":"Unsuccessful evaluation"
|
||||
},
|
||||
"DESCRIPTION-TEMPLATE-LIST": {
|
||||
"TITLE": "Available Description Templates",
|
||||
|
@ -1962,6 +1969,20 @@
|
|||
"MESSAGE": "Somebody else is modifying the Plan at this moment. You may view the Plan but you cannot make any changes."
|
||||
}
|
||||
},
|
||||
"PLAN-EVALUATE-DIALOG": {
|
||||
"HEADER": "Ranking Information",
|
||||
"DETAILS-SUB-HEADER": "Details",
|
||||
"RANK-BODY": "Rank",
|
||||
"DETAILS-BODY": "Details",
|
||||
"MESSAGES-BODY": "Messages"
|
||||
},
|
||||
"DESCRIPTION-EVALUATE-DIALOG": {
|
||||
"HEADER": "Ranking Information",
|
||||
"DETAILS-SUB-HEADER": "Details",
|
||||
"RANK-BODY": "Rank",
|
||||
"DETAILS-BODY": "Details",
|
||||
"MESSAGES-BODY": "Messages"
|
||||
},
|
||||
"REFERENCE-FIELD": {
|
||||
"COULD-NOT-FIND-MESSAGE": "Couldn't find it?",
|
||||
"ACTIONS": {
|
||||
|
|
Loading…
Reference in New Issue