diff --git a/api/src/engine/connectors/exareme/converters.ts b/api/src/engine/connectors/exareme/converters.ts index a1c28d5727aa9e2b082b1abbf072a5eb7dd4b75e..b25282036fd29e191bf130d7779b8c9fe58f07b6 100644 --- a/api/src/engine/connectors/exareme/converters.ts +++ b/api/src/engine/connectors/exareme/converters.ts @@ -221,6 +221,25 @@ export const dataToExperiment = ( .flat() : []; + const allVariables = exp.filterVariables || []; + + // add filter variables + const extractVariablesFromFilter = (filter: any): any => + filter.rules.forEach((r: any) => { + if (r.rules) { + extractVariablesFromFilter(r); + } + if (r.id) { + allVariables.push(r.id); + } + }); + + if (exp && exp.filter) { + extractVariablesFromFilter(JSON.parse(exp.filter)); + } + + exp.filterVariables = Array.from(new Set(allVariables)); + return exp; } catch (e) { return { diff --git a/api/src/engine/connectors/exareme/main.connector.ts b/api/src/engine/connectors/exareme/main.connector.ts index e0bde6339049df9cd4566a177d817b507750337e..ea9393214386c4002fe0f3c7f67a81d4dc9338e6 100644 --- a/api/src/engine/connectors/exareme/main.connector.ts +++ b/api/src/engine/connectors/exareme/main.connector.ts @@ -23,6 +23,7 @@ import { PartialExperiment, } from 'src/engine/models/experiment/experiment.model'; import { ListExperiments } from 'src/engine/models/experiment/list-experiments.model'; +import { FormulaOperation } from 'src/engine/models/formula/formula-operation.model'; import { Group } from 'src/engine/models/group.model'; import { Variable } from 'src/engine/models/variable.model'; import { ExperimentCreateInput } from 'src/experiments/models/input/experiment-create.input'; @@ -52,6 +53,19 @@ export default class ExaremeService implements IEngineService { private readonly httpService: HttpService, ) {} + async getFormulaConfiguration(): Promise<FormulaOperation[]> { + return [ + { + variableType: 'real', + operationTypes: ['log', 'exp', 'center', 'standardize'], + }, + { + variableType: 'nominal', + operationTypes: ['dummy', 'poly', 'contrast', 'additive'], + }, + ]; + } + getConfiguration(): IConfiguration { return { contactLink: 'https://ebrains.eu/support/', diff --git a/api/src/engine/engine.interfaces.ts b/api/src/engine/engine.interfaces.ts index 08fb86116b02282799d31c6c987e7006b1ac2b3a..f1cf66f1ba0623186e6213cea3b087e150593a51 100644 --- a/api/src/engine/engine.interfaces.ts +++ b/api/src/engine/engine.interfaces.ts @@ -13,6 +13,8 @@ import { ExperimentCreateInput } from '../experiments/models/input/experiment-cr import { ExperimentEditInput } from '../experiments/models/input/experiment-edit.input'; import { ListExperiments } from './models/experiment/list-experiments.model'; import { ResultUnion } from './models/result/common/result-union.model'; +import { FormulaOperation } from './models/formula/formula-operation.model'; +import { FilterConfiguration } from './models/filter/filter-configuration'; export interface IEngineOptions { type: string; @@ -122,6 +124,22 @@ export interface IEngineService { data?: UpdateUserInput, ): Promise<User | undefined>; + /** + * This is a method that is used to get the list of formula operations + * that are available in the engine. + * @param req - Request - Optional request object from the HTTP request + * @returns - Formula configuration + */ + getFormulaConfiguration?(req?: Request): Promise<FormulaOperation[]>; + + /** + * This is a method that is used to get the filter configuration + * that is available in the engine. + * @param req - Request - Optional request object from the HTTP request + * @returns Filter configuration + */ + getFilterConfiguration?(req?: Request): Promise<FilterConfiguration[]>; + /** * Perform a logout on the current logged in user * @param req - Request - this is the request object from the HTTP request. diff --git a/api/src/engine/engine.resolver.ts b/api/src/engine/engine.resolver.ts index 78b2abd48568732644e6e5dfe45dd32715987e6a..9a20cc5db5cfca459099aeabbc722e824cc55085 100644 --- a/api/src/engine/engine.resolver.ts +++ b/api/src/engine/engine.resolver.ts @@ -19,6 +19,8 @@ import { ErrorsInterceptor } from './interceptors/errors.interceptor'; import { Configuration } from './models/configuration.model'; import { Domain } from './models/domain.model'; import { Algorithm } from './models/experiment/algorithm.model'; +import { FilterConfiguration } from './models/filter/filter-configuration'; +import { FormulaOperation } from './models/formula/formula-operation.model'; @UseInterceptors(ErrorsInterceptor) @UseGuards(GlobalAuthGuard) @@ -73,4 +75,20 @@ export class EngineResolver { async algorithms(@GQLRequest() req: Request) { return this.engineService.getAlgorithms(req); } + + @Query(() => [FormulaOperation]) + async formula() { + if (this.engineService.getFormulaConfiguration) + return this.engineService.getFormulaConfiguration(); + + return []; + } + + @Query(() => FilterConfiguration) + async filter() { + if (this.engineService.getFilterConfiguration) + return this.engineService.getFilterConfiguration(); + + return []; + } } diff --git a/api/src/engine/models/filter/filter-configuration.ts b/api/src/engine/models/filter/filter-configuration.ts new file mode 100644 index 0000000000000000000000000000000000000000..436057040645da932d133516a312fc3705d09ead --- /dev/null +++ b/api/src/engine/models/filter/filter-configuration.ts @@ -0,0 +1,11 @@ +import { Field, ObjectType } from '@nestjs/graphql'; + +@ObjectType() +export class FilterConfiguration { + @Field(() => [String], { + description: 'List of types that can considered as number', + defaultValue: ['real', 'integer'], + nullable: true, + }) + numberTypes?: string[]; +} diff --git a/api/src/engine/models/formula/formula-operation.model.ts b/api/src/engine/models/formula/formula-operation.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..82077516b6d9d826461e3f63795d1bc39af4397e --- /dev/null +++ b/api/src/engine/models/formula/formula-operation.model.ts @@ -0,0 +1,12 @@ +import { Field, ObjectType } from '@nestjs/graphql'; + +@ObjectType() +export class FormulaOperation { + @Field({ description: 'Type name of the variable' }) + variableType: string; + + @Field(() => [String], { + description: 'List of operation available for this type', + }) + operationTypes: string[]; +} diff --git a/api/src/schema.gql b/api/src/schema.gql index bacb2edfe2278a64df352541366d29daab10b7a9..4311e4d617818b7b53c7ac2253fdb66c8fb47f1c 100644 --- a/api/src/schema.gql +++ b/api/src/schema.gql @@ -158,6 +158,19 @@ type Algorithm { description: String } +type FilterConfiguration { + """List of types that can considered as number""" + numberTypes: [String!] +} + +type FormulaOperation { + """Type name of the variable""" + variableType: String! + + """List of operation available for this type""" + operationTypes: [String!]! +} + type GroupResult { name: String! description: String @@ -358,6 +371,8 @@ type Query { configuration: Configuration! domains(ids: [String!] = []): [Domain!]! algorithms: [Algorithm!]! + formula: [FormulaOperation!]! + filter: FilterConfiguration! user: User! experimentList(page: Float = 0, name: String = ""): ListExperiments! experiment(id: String!): Experiment! diff --git a/api/src/users/interceptors/users.interceptor.ts b/api/src/users/interceptors/users.interceptor.ts index e41804cd4db4379cb14d60d20a484502d3010134..d0fd2785ebd82d8823a3163ad7e5f10a936ea00a 100644 --- a/api/src/users/interceptors/users.interceptor.ts +++ b/api/src/users/interceptors/users.interceptor.ts @@ -20,6 +20,9 @@ export class UsersInterceptor implements NestInterceptor { const ctx = GqlExecutionContext.create(context); const req = ctx.getContext().req ?? ctx.switchToHttp().getRequest(); + if (req.userExtended) return next.handle(); // user already extended + req.userExtended = true; + const user: User = req.user; if (user && user.id) { await this.usersService.extendedUser(user); diff --git a/api/src/users/users.service.ts b/api/src/users/users.service.ts index bcf4317bf96a46af2ad81c17684d3f8748b6d6aa..3138f9a5e748bb329b154d9ac3e8f9319213066f 100644 --- a/api/src/users/users.service.ts +++ b/api/src/users/users.service.ts @@ -60,7 +60,7 @@ export class UsersService { } catch (err) { if (err instanceof NotFoundException) this.logger.debug( - `Extension of ${user.id} aborted, no user found in database`, + `Extension of ${user.id} not needed, user not found in database`, ); } }