Skip to content
Snippets Groups Projects
Commit 6519376e authored by Steve Reis's avatar Steve Reis
Browse files

Merge branch '38-add-algorithm-resources-in-graphql' into 'develop'

Resolve "Add Algorithm resources in GraphQL"

Closes #38

See merge request sibmip/gateway!16
parents 8472bee5 b2490ced
No related branches found
No related tags found
No related merge requests found
Showing
with 142 additions and 41 deletions
......@@ -8,8 +8,12 @@ import {
} from 'src/engine/models/experiment/experiment.model';
import { ListExperiments } from 'src/engine/models/experiment/list-experiments.model';
import { ExperimentEditInput } from 'src/engine/models/experiment/input/experiment-edit.input';
import { Algorithm } from 'src/engine/models/experiment/algorithm.model';
export default class DataShieldService implements IEngineService {
getAlgorithms(): Algorithm[] | Promise<Algorithm[]> {
throw new Error('Method not implemented.');
}
createExperiment(
data: ExperimentCreateInput,
isTransient: boolean,
......@@ -48,7 +52,7 @@ export default class DataShieldService implements IEngineService {
throw new Error('Method not implemented.');
}
getExperimentAPI(): Observable<string> {
getExperimentREST(): Observable<string> {
throw new Error('Method not implemented.');
}
......@@ -56,7 +60,7 @@ export default class DataShieldService implements IEngineService {
throw new Error('Method not implemented.');
}
editExperimentAPI(): Observable<string> {
editExperimentREST(): Observable<string> {
throw new Error('Method not implemented.');
}
......@@ -72,7 +76,7 @@ export default class DataShieldService implements IEngineService {
throw new Error('Method not implemented.');
}
getAlgorithms(): Observable<string> {
getAlgorithmsREST(): Observable<string> {
throw new Error('Method not implemented.');
}
}
import { Category } from 'src/engine/models/category.model';
import { AlgorithmParameter } from 'src/engine/models/experiment/algorithm-parameter.model';
import { Algorithm } from 'src/engine/models/experiment/algorithm.model';
import { Experiment } from 'src/engine/models/experiment/experiment.model';
import { ExperimentCreateInput } from 'src/engine/models/experiment/input/experiment-create.input';
import { Group } from 'src/engine/models/group.model';
......@@ -18,6 +19,7 @@ import { VariableEntity } from './interfaces/variable-entity.interface';
import {
descriptiveModelToTables,
descriptiveSingleToTables,
transformToAlgorithms,
transformToExperiment,
} from './transformations';
......@@ -92,7 +94,8 @@ export const descriptiveDataToTableResult = (
result.groups = [
new GroupResult({
name: 'Single',
name: 'Variables',
description: 'Descriptive statistics for the variables of interest.',
results: descriptiveSingleToTables.evaluate(data),
}),
];
......@@ -100,6 +103,8 @@ export const descriptiveDataToTableResult = (
result.groups.push(
new GroupResult({
name: 'Model',
description:
'Intersection table for the variables of interest as it appears in the experiment.',
results: descriptiveModelToTables.evaluate(data),
}),
);
......@@ -124,10 +129,14 @@ export const dataToExperiment = (data: ExperimentData): Experiment => {
return exp;
};
export const dataToAlgorithms = (data: string): Algorithm[] => {
return transformToAlgorithms.evaluate(data);
};
export const dataToRaw = (result: ResultExperiment): RawResult[] => {
return [
{
data: result.data,
rawdata: result.data,
},
];
};
......
......@@ -4,6 +4,7 @@ import { Request } from 'express';
import { firstValueFrom, map, Observable } from 'rxjs';
import { IEngineOptions, IEngineService } from 'src/engine/engine.interfaces';
import { Domain } from 'src/engine/models/domain.model';
import { Algorithm } from 'src/engine/models/experiment/algorithm.model';
import {
Experiment,
PartialExperiment,
......@@ -14,6 +15,7 @@ import { ListExperiments } from 'src/engine/models/experiment/list-experiments.m
import { Group } from 'src/engine/models/group.model';
import { Variable } from 'src/engine/models/variable.model';
import {
dataToAlgorithms,
dataToCategory,
dataToExperiment,
dataToGroup,
......@@ -60,6 +62,14 @@ export default class ExaremeService implements IEngineService {
};
}
async getAlgorithms(): Promise<Algorithm[]> {
const path = this.options.baseurl + 'algorithms';
const resultAPI = await firstValueFrom(this.httpService.get<string>(path));
return dataToAlgorithms(resultAPI.data);
}
async getExperiment(uuid: string): Promise<Experiment> {
const path = this.options.baseurl + `experiments/${uuid}`;
......@@ -144,7 +154,7 @@ export default class ExaremeService implements IEngineService {
.pipe(map((response) => response.data));
}
getExperimentAPI(uuid: string): Observable<string> {
getExperimentREST(uuid: string): Observable<string> {
const path = this.options.baseurl + `experiments/${uuid}`;
return this.httpService
......@@ -158,7 +168,7 @@ export default class ExaremeService implements IEngineService {
return this.httpService.delete(path).pipe(map((response) => response.data));
}
editExperimentAPI(uuid: string, request: Request): Observable<string> {
editExperimentREST(uuid: string, request: Request): Observable<string> {
const path = this.options.baseurl + `experiments/${uuid}`;
return this.httpService
......@@ -190,7 +200,7 @@ export default class ExaremeService implements IEngineService {
.pipe(map((response) => response.data));
}
getAlgorithms(request: Request): Observable<string> {
getAlgorithmsREST(request: Request): Observable<string> {
const path = this.options.baseurl + 'algorithms';
return this.httpService
......
......@@ -3,6 +3,31 @@
import * as jsonata from 'jsonata'; // old import style needed due to 'export = jsonata'
export const transformToAlgorithms = jsonata(`
(
$params := ["y", "pathology", "dataset", "filter"];
$toArray := function($x) { $type($x) = 'array' ? $x : [$x]};
*.{
'name': name,
'label': label,
'description': desc,
'parameters': $toArray(parameters[$not(name in $params)].{
'name': name,
'description': desc,
'label': label,
'type': valueType,
'defaultValue': defaultValue,
'isMultiple': $boolean(valueMultiple),
'isRequired': $boolean(valueNotBlank),
'min': valueMin,
'max': valueMax
})
}
)
`);
export const transformToExperiment = jsonata(`
(
$params := ["y", "pathology", "dataset", "filter"];
......
......@@ -21,7 +21,7 @@ export class EngineController {
@Get('/algorithms')
getAlgorithms(@Req() request: Request): Observable<string> {
return this.engineService.getAlgorithms(request);
return this.engineService.getAlgorithmsREST(request);
}
@Get('/experiments')
......@@ -31,7 +31,7 @@ export class EngineController {
@Get('/experiments/:uuid')
getExperiment(@Param('uuid') uuid: string): Observable<string> {
return this.engineService.getExperimentAPI(uuid);
return this.engineService.getExperimentREST(uuid);
}
@Delete('/experiments/:uuid')
......@@ -47,7 +47,7 @@ export class EngineController {
@Param('uuid') uuid: string,
@Req() request: Request,
): Observable<string> {
return this.engineService.editExperimentAPI(uuid, request);
return this.engineService.editExperimentREST(uuid, request);
}
@Post('experiments/transient')
......
import { Request } from 'express';
import { Observable } from 'rxjs';
import { Domain } from './models/domain.model';
import { Algorithm } from './models/experiment/algorithm.model';
import {
Experiment,
PartialExperiment,
......@@ -39,16 +40,18 @@ export interface IEngineService {
expriment: ExperimentEditInput,
): Promise<Experiment> | Experiment;
getAlgorithms(): Promise<Algorithm[]> | Algorithm[];
// Standard REST API call
getAlgorithms(request: Request): Observable<string>;
getAlgorithmsREST(request: Request): Observable<string>;
getExperiments(request: Request): Observable<string>;
getExperimentAPI(uuid: string): Observable<string>;
getExperimentREST(uuid: string): Observable<string>;
deleteExperiment(uuid: string, request: Request): Observable<string>;
editExperimentAPI(uuid: string, request: Request): Observable<string>;
editExperimentREST(uuid: string, request: Request): Observable<string>;
startExperimentTransient(request: Request): Observable<string>;
......
......@@ -3,6 +3,7 @@ import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
import { ENGINE_SERVICE } from './engine.constants';
import { IEngineService } from './engine.interfaces';
import { Domain } from './models/domain.model';
import { Algorithm } from './models/experiment/algorithm.model';
import {
Experiment,
PartialExperiment,
......@@ -38,6 +39,11 @@ export class EngineResolver {
return this.engineService.getExperiment(uuid);
}
@Query(() => [Algorithm])
async algorithms() {
return this.engineService.getAlgorithms();
}
@Mutation(() => Experiment)
async createExperiment(
@Args('data') experimentCreateInput: ExperimentCreateInput,
......
......@@ -5,6 +5,30 @@ export class AlgorithmParameter {
@Field()
name: string;
@Field(() => [String])
value: string[];
@Field(() => [String], { nullable: true })
value?: string[];
@Field({ nullable: true })
label?: string;
@Field({ nullable: true })
description?: string;
@Field({ nullable: true })
defaultValue?: string;
@Field({ defaultValue: false, nullable: true })
isMultiple?: boolean;
@Field({ defaultValue: false, nullable: true })
isRequired?: boolean;
@Field({ nullable: true })
min?: string;
@Field({ nullable: true })
max?: string;
@Field({ nullable: true })
type?: string;
}
......@@ -7,8 +7,14 @@ export class Algorithm {
name: string;
@Field(() => [AlgorithmParameter], { nullable: true, defaultValue: [] })
parameters: AlgorithmParameter[];
parameters?: AlgorithmParameter[];
@Field()
type: string;
@Field({ nullable: true })
label?: string;
@Field({ nullable: true })
type?: string;
@Field({ nullable: true })
description?: string;
}
......@@ -11,7 +11,7 @@ export const ResultUnion = createUnionType({
return TableResult;
}
if (value.listMax) {
if (value.rawdata) {
return RawResult;
}
......
......@@ -11,6 +11,9 @@ export class GroupResult {
@Field()
name: string;
@Field({ nullable: true })
description?: string;
@Field(() => [ResultUnion])
results: Array<typeof ResultUnion>;
}
......
import { Field, ObjectType } from '@nestjs/graphql';
import { GraphQLJSONObject } from 'graphql-type-json';
import GraphQLJSON from 'graphql-type-json';
import { Result } from './common/result.model';
// field name 'rawdata' was used instead of 'data' because of typing problem on union with same field name
// see :https://stackoverflow.com/questions/44170603/graphql-using-same-field-names-in-different-types-within-union
@ObjectType()
export class RawResult extends Result {
@Field(() => GraphQLJSONObject)
data: unknown;
@Field(() => [String], { defaultValue: [] })
listMax?: string[];
@Field(() => GraphQLJSON)
rawdata: unknown;
}
......@@ -34,8 +34,30 @@ type Domain {
rootGroup: Group!
}
type AlgorithmParameter {
name: String!
value: [String!]
label: String
description: String
defaultValue: String
isMultiple: Boolean
isRequired: Boolean
min: String
max: String
type: String
}
type Algorithm {
name: String!
parameters: [AlgorithmParameter!]
label: String
type: String
description: String
}
type GroupResult {
name: String!
description: String
results: [ResultUnion!]!
}
......@@ -48,14 +70,13 @@ type TableResult {
}
type RawResult {
data: JSONObject!
listMax: [String!]!
rawdata: JSON!
}
"""
The `JSONObject` scalar type represents JSON objects as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).
The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).
"""
scalar JSONObject @specifiedBy(url: "http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf")
scalar JSON @specifiedBy(url: "http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf")
type GroupsResult {
groups: [GroupResult!]!
......@@ -66,17 +87,6 @@ type Header {
type: String!
}
type AlgorithmParameter {
name: String!
value: [String!]!
}
type Algorithm {
name: String!
parameters: [AlgorithmParameter!]
type: String!
}
type Experiment {
uuid: String
author: String
......@@ -124,6 +134,7 @@ type Query {
domains(ids: [String!] = []): [Domain!]!
experiments(name: String = "", page: Float = 0): ListExperiments!
expriment(uuid: String!): Experiment!
algorithms: [Algorithm!]!
}
type Mutation {
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment