Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 12 additions & 18 deletions backend/packages/Upgrade/src/api/repositories/LogRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import { LevelCombinationElement } from '../models/LevelCombinationElement';
import { ExperimentCondition } from '../models/ExperimentCondition';
import { QueryRepository } from './QueryRepository';
import { MetricRepository } from './MetricRepository';
import { IndividualEnrollmentRepository } from './IndividualEnrollmentRepository';
import { RepeatedEnrollment } from '../models/RepeatedEnrollment';
import { IndividualEnrollmentRepository } from './IndividualEnrollmentRepository';
@EntityRepository(Log)
export class LogRepository extends Repository<Log> {
public async deleteExceptByIds(values: string[], entityManager: EntityManager): Promise<Log[]> {
Expand Down Expand Up @@ -196,16 +196,14 @@ export class LogRepository extends Repository<Log> {
isFactorialExperiment: boolean,
isCategorical: boolean,
repeatedMeasure: REPEATED_MEASURE,
query: any,
transactionalEntityManager: EntityManager
query: any
) {
const individualEnrollmentRepo = transactionalEntityManager.withRepository(
Container.getCustomRepository(IndividualEnrollmentRepository, 'export')
);
const individualEnrollmentRepo = Container.getCustomRepository(IndividualEnrollmentRepository, 'export');

const innerQuery = individualEnrollmentRepo.createQueryBuilder('individualEnrollment');

const analyticsQuery = transactionalEntityManager.createQueryBuilder();
const middleQuery = transactionalEntityManager.createQueryBuilder();
const analyticsQuery = Container.getDataSource('export').createQueryBuilder();
const middleQuery = Container.getDataSource('export').createQueryBuilder();

const idToSelect = isFactorialExperiment ? '"levelId"' : '"conditionId"';
const valueToSelect = isFactorialExperiment
Expand Down Expand Up @@ -295,12 +293,10 @@ export class LogRepository extends Repository<Log> {
isFactorialExperiment: boolean,
isCategorical: boolean,
repeatedMeasure: REPEATED_MEASURE,
query: any,
transactionalEntityManager: EntityManager
query: any
) {
const individualEnrollmentRepo = transactionalEntityManager.withRepository(
Container.getCustomRepository(IndividualEnrollmentRepository, 'export')
);
const individualEnrollmentRepo = Container.getCustomRepository(IndividualEnrollmentRepository, 'export');

const analyticsQuery = individualEnrollmentRepo.createQueryBuilder('individualEnrollment');

const idToSelect = isFactorialExperiment
Expand Down Expand Up @@ -369,7 +365,7 @@ export class LogRepository extends Repository<Log> {
return analyticsQuery;
}

public async analysis(query: Query, transactionalEntityManager: EntityManager): Promise<any> {
public async analysis(query: Query): Promise<any> {
const {
metric: { key: metricKey, type: metricType },
experiment: { id: experimentId, assignmentUnit: unitOfAssignment, type: experimentType },
Expand All @@ -391,8 +387,7 @@ export class LogRepository extends Repository<Log> {
isFactorialExperiment,
!isContinuousMetric,
repeatedMeasure,
query.query,
transactionalEntityManager
query.query
)
: this.getWithinSubjectsAnalyticsQuery(
experimentId,
Expand All @@ -401,8 +396,7 @@ export class LogRepository extends Repository<Log> {
isFactorialExperiment,
!isContinuousMetric,
repeatedMeasure,
query.query,
transactionalEntityManager
query.query
);

return newQuery.getRawMany();
Expand Down
65 changes: 26 additions & 39 deletions backend/packages/Upgrade/src/api/services/QueryService.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Service } from 'typedi';
import { InjectDataSource, InjectRepository } from '../../typeorm-typedi-extensions';
import { Container } from './../../typeorm-typedi-extensions/Container';
import { QueryRepository } from '../repositories/QueryRepository';
import { Query } from '../models/Query';
import { LogRepository } from '../repositories/LogRepository';
Expand Down Expand Up @@ -53,56 +52,44 @@ export class QueryService {

public async analyze(queryIds: string[], logger: UpgradeLogger): Promise<any> {
logger.info({ message: `Get analysis of query with queryIds ${queryIds}` });
const promiseArray = queryIds.map((queryId) =>
this.queryRepository.findOne({
where: { id: queryId },
relations: [
'metric',
'experiment',
'experiment.conditions',
'experiment.partitions',
'experiment.factors',
'experiment.factors.levels',
],
})
);

const promiseResult = await Promise.all(promiseArray);
const queryList = await this.queryRepository.find({
where: { id: In(queryIds) },
relations: [
'metric',
'experiment',
'experiment.conditions',
'experiment.partitions',
'experiment.factors',
'experiment.factors.levels',
],
});
const experiments: Experiment[] = [];

// checks for archieve state experiment
if (queryIds.length !== 0) {
if (promiseResult[0].experiment?.state === EXPERIMENT_STATE.ARCHIVED) {
if (queryList[0]?.experiment?.state === EXPERIMENT_STATE.ARCHIVED) {
return this.getArchivedStats(queryIds, logger);
}
} else {
return [];
}

const customDataSource = Container.getDataSource('export');

const transactionPromise = await customDataSource.transaction(async (transactionalEntityManager) => {
const analyzePromise = promiseResult.map((query) => {
experiments.push(query.experiment);
if (query.experiment?.type === EXPERIMENT_TYPE.FACTORIAL) {
return [
this.logRepository.analysis(query, transactionalEntityManager),
this.logRepository.analysis(
{
...query,
experiment: { ...query.experiment, type: EXPERIMENT_TYPE.SIMPLE },
},
transactionalEntityManager
),
];
}
return [this.logRepository.analysis(query, transactionalEntityManager)];
});
return analyzePromise;
const analyzePromise = queryList.map((query) => {
experiments.push(query.experiment);
if (query.experiment?.type === EXPERIMENT_TYPE.FACTORIAL) {
return [
this.logRepository.analysis(query),
this.logRepository.analysis({
...query,
experiment: { ...query.experiment, type: EXPERIMENT_TYPE.SIMPLE },
}),
];
}
return [this.logRepository.analysis(query)];
});

const response = await Promise.all(
transactionPromise.map(async (queryArray) => {
analyzePromise.map(async (queryArray) => {
return await Promise.allSettled(queryArray);
})
);
Expand Down Expand Up @@ -147,7 +134,7 @@ export class QueryService {
mainEffect: queryResult[],
interactionEffect: queryResult[]
): [queryResult[], queryResult[]] {
const conditionIds = experiment?.conditions.map((condition) => condition.id) || [];
const conditionIds = experiment?.conditions?.map((condition) => condition.id) || [];

if (interactionEffect) {
conditionIds.forEach((conditionId) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,15 @@ beforeEach(() => {

manager = {
createQueryBuilder: repo.createQueryBuilder,
withRepository: jest.fn().mockReturnValue(individualEnrollmentRepo),
connection: {
getRepository: jest.fn().mockReturnValue(individualEnrollmentRepo),
},
};
});

afterEach(() => {
jest.clearAllMocks();
jest.restoreAllMocks();
});

describe('LogRepository Testing', () => {
Expand Down Expand Up @@ -309,8 +312,9 @@ describe('LogRepository Testing', () => {

// TODO: Work in progress
it('should analyse a continuous simple metric sum', async () => {
jest.spyOn(Container, 'getCustomRepository').mockReturnValueOnce(individualEnrollmentRepo);

jest.spyOn(Container, 'getCustomRepository').mockImplementation(() => {
return individualEnrollmentRepo;
});
const q = new Query();
q.id = 'id1';
q.name = 'Average Time';
Expand All @@ -327,9 +331,7 @@ describe('LogRepository Testing', () => {
compareValue: '10',
};

const res = await repo.analysis(q, manager);

expect(Container.getCustomRepository).toHaveBeenCalledWith(IndividualEnrollmentRepository, 'export');
const res = await repo.analysis(q);

expect(individualEnrollmentRepo.createQueryBuilder).toHaveBeenCalledTimes(1);

Expand All @@ -342,7 +344,9 @@ describe('LogRepository Testing', () => {
});

it('should analyse a continuous simple metric median', async () => {
jest.spyOn(Container, 'getCustomRepository').mockReturnValueOnce(individualEnrollmentRepo);
jest.spyOn(Container, 'getCustomRepository').mockImplementation(() => {
return individualEnrollmentRepo;
});

const q = new Query();
q.id = 'id1';
Expand All @@ -360,9 +364,7 @@ describe('LogRepository Testing', () => {
compareValue: '10',
};

const res = await repo.analysis(q, manager);

expect(Container.getCustomRepository).toHaveBeenCalledWith(IndividualEnrollmentRepository, 'export');
const res = await repo.analysis(q);

expect(individualEnrollmentRepo.createQueryBuilder).toHaveBeenCalledTimes(1);

Expand All @@ -376,8 +378,9 @@ describe('LogRepository Testing', () => {
});

it('should analyse a continuous simple metric mode', async () => {
jest.spyOn(Container, 'getCustomRepository').mockReturnValueOnce(individualEnrollmentRepo);

jest.spyOn(Container, 'getCustomRepository').mockImplementation(() => {
return individualEnrollmentRepo;
});
const q = new Query();
q.id = 'id1';
q.name = 'Average Time';
Expand All @@ -394,9 +397,7 @@ describe('LogRepository Testing', () => {
compareValue: '10',
};

const res = await repo.analysis(q, manager);

expect(Container.getCustomRepository).toHaveBeenCalledWith(IndividualEnrollmentRepository, 'export');
const res = await repo.analysis(q);

expect(individualEnrollmentRepo.createQueryBuilder).toHaveBeenCalledTimes(1);

Expand All @@ -410,8 +411,9 @@ describe('LogRepository Testing', () => {
});

it('should analyse a continuous simple metric count', async () => {
jest.spyOn(Container, 'getCustomRepository').mockReturnValueOnce(individualEnrollmentRepo);

jest.spyOn(Container, 'getCustomRepository').mockImplementation(() => {
return individualEnrollmentRepo;
});
const q = new Query();
q.id = 'id1';
q.name = 'Average Time';
Expand All @@ -428,9 +430,7 @@ describe('LogRepository Testing', () => {
compareValue: '10',
};

const res = await repo.analysis(q, manager);

expect(Container.getCustomRepository).toHaveBeenCalledWith(IndividualEnrollmentRepository, 'export');
const res = await repo.analysis(q);

expect(individualEnrollmentRepo.createQueryBuilder).toHaveBeenCalledTimes(1);

Expand All @@ -443,8 +443,12 @@ describe('LogRepository Testing', () => {
});

it('should analyse a continuous repeated metric most recent avg', async () => {
jest.spyOn(Container, 'getCustomRepository').mockReturnValueOnce(individualEnrollmentRepo);

jest.spyOn(Container, 'getCustomRepository').mockImplementation(() => {
return individualEnrollmentRepo;
});
jest.spyOn(Container, 'getDataSource').mockImplementation(() => {
return dataSource;
});
const q = new Query();
q.id = 'id1';
q.name = 'Average Time';
Expand All @@ -462,9 +466,7 @@ describe('LogRepository Testing', () => {
};
q.repeatedMeasure = REPEATED_MEASURE.mostRecent;

const res = await repo.analysis(q, manager);

expect(Container.getCustomRepository).toHaveBeenCalledWith(IndividualEnrollmentRepository, 'export');
const res = await repo.analysis(q);

expect(individualEnrollmentRepo.createQueryBuilder).toHaveBeenCalledTimes(1);

Expand All @@ -478,8 +480,12 @@ describe('LogRepository Testing', () => {
});

it('should analyse a categorical repeated metric earliest percentage', async () => {
jest.spyOn(Container, 'getCustomRepository').mockReturnValueOnce(individualEnrollmentRepo);

jest.spyOn(Container, 'getCustomRepository').mockImplementation(() => {
return individualEnrollmentRepo;
});
jest.spyOn(Container, 'getDataSource').mockImplementation(() => {
return dataSource;
});
const data1 = {
conditionId: 1,
result: 10,
Expand Down Expand Up @@ -507,9 +513,7 @@ describe('LogRepository Testing', () => {
};
q.repeatedMeasure = REPEATED_MEASURE.earliest;

const res = await repo.analysis(q, manager);

expect(Container.getCustomRepository).toHaveBeenCalledWith(IndividualEnrollmentRepository, 'export');
const res = await repo.analysis(q);

expect(individualEnrollmentRepo.createQueryBuilder).toHaveBeenCalledTimes(1);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ describe('Query Service Testing', () => {
});

it('should find and map results to queries', async () => {
queryRepo.find = jest.fn().mockResolvedValue([mockquery1]);
const response = await service.analyze(['id1'], logger);
expect(response).toEqual([
{
Expand All @@ -167,7 +168,7 @@ describe('Query Service Testing', () => {
});

it('should log error when query fails', async () => {
queryRepo.findOne = jest.fn().mockResolvedValue([]);
queryRepo.find = jest.fn().mockResolvedValue(['nothing']);
const response = await service.analyze(['id1'], logger);
expect(response).toEqual([
{
Expand Down