Skip to content

Commit

Permalink
Merge branch 'dev' into dependabot/npm_and_yarn/backend/packages/Upgr…
Browse files Browse the repository at this point in the history
…ade/multi-86577ad91c
  • Loading branch information
danoswaltCL authored Dec 11, 2024
2 parents c28aba1 + 2a61beb commit 98b4f1b
Show file tree
Hide file tree
Showing 28 changed files with 224 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1286,7 +1286,38 @@ export class ExperimentController {
@Req() request: AppRequest
): Promise<Experiment[]> {
const experimentIds = params.ids;
return this.experimentService.exportExperiment(experimentIds, currentUser, request.logger);
return this.experimentService.exportExperiment(currentUser, request.logger, experimentIds);
}

/**
* @swagger
* /experiments/all:
* get:
* description: Export All Experiment JSON
* tags:
* - Experiments
* produces:
* - application/json
* responses:
* '200':
* description: Experiments are exported
* schema:
* type: array
* items:
* type: object
* properties:
* fileName:
* type: string
* error:
* type: string
* '401':
* description: AuthorizationRequiredError
* '500':
* description: Internal Server Error
*/
@Get('/export/all')
public exportAllExperiment(@CurrentUser() currentUser: UserDTO, @Req() request: AppRequest): Promise<Experiment[]> {
return this.experimentService.exportExperiment(currentUser, request.logger);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ export class ExperimentAssignmentService {
groupExperiments
);
}

invalidGroupExperiment = await this.groupExperimentWithoutEnrollments(
isGroupWorkingGroupMissing ? groupExperiments : experimentWithInvalidGroupOrWorkingGroup,
experimentUser,
Expand Down Expand Up @@ -826,13 +827,44 @@ export class ExperimentAssignmentService {
return groupExperimentAssignedIds.indexOf(experiment.id) === -1;
});

const experimentToExcludeIds = experimentToExclude.map((experiment) => experiment.id);

// throw error user group not defined and add experiments which are excluded
experimentToExclude.forEach(({ id, name }) => {
logger.error({
message: `Experiment Id: ${id},
Experiment Name: ${name},
Group not valid for experiment user
`,
});
});

// log error if there are group experiments which are not previosly assigned and they are
// to be excluded from assignment due to working group and group data is not properly set
if (experimentToExclude.length > 0) {
logger.error(
{
endPoint: '/api/assign',
errorCode: 417,
message: `Group not defined for experiment User: ${JSON.stringify(
{ ...experimentUser, experiment: experimentToExcludeIds },
undefined,
2
)}`,
name: 'Experiment user group not defined',
type: SERVER_ERROR.EXPERIMENT_USER_GROUP_NOT_DEFINED,
} as any,
logger
);
}
return experimentToExclude;
}

private experimentsWithInvalidGroupAndWorkingGroup(user: ExperimentUser, experiments: Experiment[]): Experiment[] {
return experiments.filter((experiment) => {
const { group } = experiment;
if (group in user.group && group in user.workingGroup) {
// filter the invalid experiment for which the user's working group is not present in the user's group
return !user.group[group].includes(user.workingGroup[group]);
} else {
return true;
Expand Down
14 changes: 9 additions & 5 deletions backend/packages/Upgrade/src/api/services/ExperimentService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,7 @@ export class ExperimentService {
let queryBuilder = this.experimentRepository
.createQueryBuilder('experiment')
.leftJoinAndSelect('experiment.conditions', 'conditions')
.leftJoinAndSelect('experiment.partitions', 'partitions')
.take(take)
.skip(skip);
.leftJoinAndSelect('experiment.partitions', 'partitions');

if (searchParams) {
const customSearchString = searchParams.string.split(' ').join(`:*&`);
Expand All @@ -169,11 +167,17 @@ export class ExperimentService {
.addSelect(`ts_rank_cd(to_tsvector('english',${postgresSearchString}), to_tsquery(:query))`, 'rank')
.addOrderBy('rank', 'DESC')
.setParameter('query', `${customSearchString}:*`);
}
if (sortParams) {
queryBuilder = queryBuilder.addOrderBy(`LOWER(CAST(experiment.name AS TEXT))`, sortParams.sortAs);
} else {
queryBuilder = queryBuilder.addOrderBy('experiment.updatedAt', 'DESC');
}

let expIds = (await queryBuilder.getMany()).map((exp) => exp.id);
// manually paginating the data
// there is an active issue in typeorm where we can't use skip and take with orderby
expIds = expIds.slice(skip, skip + take);
expIds = Array.from(new Set(expIds));

let queryBuilderToReturn = this.experimentRepository
Expand Down Expand Up @@ -562,10 +566,10 @@ export class ExperimentService {
return validatedExperiments;
}

public async exportExperiment(experimentIds: string[], user: UserDTO, logger: UpgradeLogger): Promise<Experiment[]> {
public async exportExperiment(user: UserDTO, logger: UpgradeLogger, experimentIds?: string[]): Promise<Experiment[]> {
logger.info({ message: `Inside export Experiment JSON ${experimentIds}` });
const experimentDetails = await this.experimentRepository.find({
where: { id: In(experimentIds) },
where: experimentIds ? { id: In(experimentIds) } : undefined,
relations: [
'partitions',
'conditions',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ export class ExperimentDataService {
return this.http.get(url, { params: ids });
}

exportAllExperimentDesign() {
const url = `${this.environment.api.exportAllExperiment}`;
return this.http.get(url);
}

fetchExperimentGraphInfo(params: any) {
const url = this.environment.api.experimentGraphInfo;
return this.http.post(url, params);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,12 @@ describe('ExperimentService', () => {

service.exportExperimentDesign([experimentId]);

expect(mockStore.dispatch).toHaveBeenCalledWith(actionExportExperimentDesign({ experimentIds: [experimentId] }));
expect(mockStore.dispatch).toHaveBeenCalledWith(
actionExportExperimentDesign({
experimentIds: [experimentId],
exportAll: false,
})
);
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
selectIsPollingExperimentDetailStats,
selectCurrentContextMetaDataConditions,
selectIsLoadingContextMetaData,
selectExperimentsExportLoading,
} from './store/experiments.selectors';
import * as experimentAction from './store//experiments.actions';
import { AppState } from '../core.state';
Expand Down Expand Up @@ -63,6 +64,7 @@ export class ExperimentService {
isLoadingExperiment$ = this.store$.pipe(select(selectIsLoadingExperiment));
isLoadingExperimentDetailStats$ = this.store$.pipe(select(selectIsLoadingExperimentDetailStats));
isPollingExperimentDetailStats$ = this.store$.pipe(select(selectIsPollingExperimentDetailStats));
isExperimentsExportLoading$ = this.store$.pipe(select(selectExperimentsExportLoading));
selectedExperiment$ = this.store$.pipe(select(selectSelectedExperiment));
allDecisionPoints$ = this.store$.pipe(select(selectAllDecisionPoints));
allExperimentNames$ = this.store$.pipe(select(selectAllExperimentNames));
Expand Down Expand Up @@ -204,7 +206,11 @@ export class ExperimentService {
}

exportExperimentDesign(experimentIds: string[]) {
this.store$.dispatch(experimentAction.actionExportExperimentDesign({ experimentIds }));
this.store$.dispatch(experimentAction.actionExportExperimentDesign({ experimentIds, exportAll: false }));
}

exportAllExperimentDesign() {
this.store$.dispatch(experimentAction.actionExportExperimentDesign({ experimentIds: [], exportAll: true }));
}

importExperiment(experiments: Experiment[]) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export const actionExportExperimentInfo = createAction(

export const actionExportExperimentDesign = createAction(
'[Experiment] Export Experiment Design',
props<{ experimentIds: string[] }>()
props<{ experimentIds: string[]; exportAll: boolean }>()
);

export const actionExportExperimentInfoSuccess = createAction('[Experiment] Export Experiment Info Success');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1352,7 +1352,12 @@ describe('ExperimentEffects', () => {
expect(resultingAction).toEqual(expectedAction);
});

actions$.next(actionExportExperimentDesign({ experimentIds: [experimentId] }));
actions$.next(
actionExportExperimentDesign({
experimentIds: [experimentId],
exportAll: false,
})
);

tick(0);
}));
Expand All @@ -1367,7 +1372,12 @@ describe('ExperimentEffects', () => {
expect(resultingAction).toEqual(expectedAction);
});

actions$.next(actionExportExperimentDesign({ experimentIds: [experimentId] }));
actions$.next(
actionExportExperimentDesign({
experimentIds: [experimentId],
exportAll: false,
})
);

tick(0);
}));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -463,10 +463,13 @@ export class ExperimentEffects {
exportExperimentDesign$ = createEffect(() =>
this.actions$.pipe(
ofType(experimentAction.actionExportExperimentDesign),
map((action) => ({ experimentIds: action.experimentIds })),
map((action) => ({ experimentIds: action.experimentIds, exportAll: action.exportAll })),
filter(({ experimentIds }) => !!experimentIds),
switchMap(({ experimentIds }) =>
this.experimentDataService.exportExperimentDesign(experimentIds).pipe(
switchMap(({ experimentIds, exportAll: allExport }) => {
const apiCall$ = allExport
? this.experimentDataService.exportAllExperimentDesign()
: this.experimentDataService.exportExperimentDesign(experimentIds);
return apiCall$.pipe(
tap(() => {
this.notificationService.showSuccess('Experiment Design JSON downloaded!');
}),
Expand All @@ -485,8 +488,8 @@ export class ExperimentEffects {
return experimentAction.actionExportExperimentDesignSuccess();
}),
catchError(() => [experimentAction.actionExportExperimentDesignFailure()])
)
)
);
})
)
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ export interface ExperimentState extends EntityState<Experiment> {
isLoadingExperiment: boolean;
isLoadingExperimentDetailStats: boolean;
isPollingExperimentDetailStats: boolean;
isLoadingExperimentExport: boolean;
skipExperiment: number;
totalExperiments: number;
searchKey: EXPERIMENT_SEARCH_KEY;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const initialState: ExperimentState = adapter.getInitialState({
isLoadingExperiment: false,
isLoadingExperimentDetailStats: false,
isPollingExperimentDetailStats: false,
isLoadingExperimentExport: false,
skipExperiment: 0,
totalExperiments: null,
searchKey: EXPERIMENT_SEARCH_KEY.ALL,
Expand Down Expand Up @@ -152,6 +153,14 @@ const reducer = createReducer(
on(experimentsAction.actionEndExperimentDetailStatsPolling, (state) => ({
...state,
isPollingExperimentDetailStats: false,
})),
on(experimentsAction.actionExportExperimentDesign, (state) => ({
...state,
isLoadingExperimentExport: true,
})),
on(experimentsAction.actionExportExperimentDesignSuccess, (state) => ({
...state,
isLoadingExperimentExport: false,
}))
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ export const selectIsGraphLoading = createSelector(selectExperimentState, (state

export const selectExperimentGraphRange = createSelector(selectExperimentState, (state) => state.graphRange);

export const selectExperimentsExportLoading = createSelector(
selectExperimentState,
(state) => state.isLoadingExperimentExport
);

export const selectExperimentGraphInfo = createSelector(
selectExperimentState,
selectExperimentGraphRange,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ describe('LocalStorageService', () => {
isLoadingExperiment: false,
isLoadingExperimentDetailStats: false,
isPollingExperimentDetailStats: false,
isLoadingExperimentExport: false,
skipExperiment: 0,
totalExperiments: null,
searchKey: EXPERIMENT_SEARCH_KEY.ALL,
Expand All @@ -50,6 +51,7 @@ describe('LocalStorageService', () => {
isLoadingExperiment: false,
isLoadingExperimentDetailStats: false,
isPollingExperimentDetailStats: false,
isLoadingExperimentExport: false,
skipExperiment: 0,
totalExperiments: null,
searchKey: null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export class LocalStorageService {
isLoadingExperiment: false,
isLoadingExperimentDetailStats: false,
isPollingExperimentDetailStats: false,
isLoadingExperimentExport: false,
skipExperiment: 0,
totalExperiments: null,
searchKey: experimentSearchKey as EXPERIMENT_SEARCH_KEY,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
</div>
</div>

<div scroll (scrolled)="fetchExperimentOnScroll()" class="experiment-list-table-container" #tableContainer>
<div (scroll)="onScroll()" class="experiment-list-table-container" #tableContainer>
<mat-progress-bar class="spinner" mode="indeterminate" *ngIf="isLoadingExperiment$ | async"></mat-progress-bar>
<table
class="experiment-list"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ export class ExperimentListComponent implements OnInit, OnDestroy, AfterViewInit
openExportAllExperimentsDialog() {
this.dialog.open(ExportModalComponent, {
panelClass: 'export-modal',
data: { experiment: this.allExperiments.data },
data: { experiment: this.allExperiments.data, exportAll: true },
});
}

Expand Down Expand Up @@ -222,6 +222,15 @@ export class ExperimentListComponent implements OnInit, OnDestroy, AfterViewInit
: '';
}

onScroll(): void {
const element = this.experimentTableContainer.nativeElement;
const atBottom = element.scrollHeight - element.scrollTop === element.clientHeight;

if (atBottom) {
this.fetchExperimentOnScroll();
}
}

fetchExperimentOnScroll() {
if (!this.isAllExperimentsFetched) {
this.experimentService.loadExperiments();
Expand Down
Loading

0 comments on commit 98b4f1b

Please sign in to comment.