Skip to content
Merged

0.8.3 #172

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
21 changes: 21 additions & 0 deletions api-dto/files/testtakers-validation.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export interface TestTakerLoginDto {
group: string;
login: string;
mode: string;
bookletCodes: string[];
}

export interface MissingPersonDto {
group: string;
login: string;
code: string;
reason: string;
}

export interface TestTakersValidationDto {
testTakersFound: boolean;
totalGroups: number;
totalLogins: number;
totalBookletCodes: number;
missingPersons: MissingPersonDto[];
}
13 changes: 13 additions & 0 deletions api-dto/files/variable-validation.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export interface InvalidVariableDto {
fileName: string;
variableId: string;
value: string;
responseId?: number;
expectedType?: string;
errorReason?: string;
}

export interface VariableValidationDto {
checkedFiles: number;
invalidVariables: InvalidVariableDto[];
}
203 changes: 199 additions & 4 deletions apps/backend/src/app/admin/workspace/workspace-files.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import { WorkspaceGuard } from './workspace.guard';
import { FileDownloadDto } from '../../../../../../api-dto/files/file-download.dto';
import { FileValidationResultDto } from '../../../../../../api-dto/files/file-validation-result.dto';
import { WorkspaceFilesService } from '../../database/services/workspace-files.service';
import { InvalidVariableDto } from '../../../../../../api-dto/files/variable-validation.dto';
import { TestTakersValidationDto } from '../../../../../../api-dto/files/testtakers-validation.dto';

@ApiTags('Admin Workspace Files')
@Controller('admin/workspace')
Expand Down Expand Up @@ -305,10 +307,6 @@ export class WorkspaceFilesController {
try {
const codingSchemeFile = await this.workspaceFilesService.getCodingSchemeByRef(workspace_id, coding_scheme_ref);

if (!codingSchemeFile) {
throw new NotFoundException(`Coding scheme file '${coding_scheme_ref}' not found in workspace ${workspace_id}`);
}

return codingSchemeFile;
} catch (error) {
if (error.status === 404) {
Expand All @@ -317,4 +315,201 @@ export class WorkspaceFilesController {
throw new InternalServerErrorException(`Error retrieving coding scheme file: ${error.message}`);
}
}

@Get(':workspace_id/files/validate-testtakers')
@ApiTags('test files validation')
@UseGuards(JwtAuthGuard, WorkspaceGuard)
@ApiOperation({ summary: 'Validate TestTakers', description: 'Validates TestTakers XML files and checks if each person from the persons table is found' })
@ApiParam({ name: 'workspace_id', type: Number, description: 'ID of the workspace' })
@ApiOkResponse({
description: 'TestTakers validation result'
})
async validateTestTakers(
@Param('workspace_id') workspace_id: number): Promise<TestTakersValidationDto> {
return this.workspaceFilesService.validateTestTakers(workspace_id);
}

@Get(':workspace_id/files/validate-group-responses')
@ApiTags('test files validation')
@UseGuards(JwtAuthGuard, WorkspaceGuard)
@ApiOperation({ summary: 'Validate group responses', description: 'Validates if there\'s at least one response for each group found in TestTakers XML files' })
@ApiParam({ name: 'workspace_id', type: Number, description: 'ID of the workspace' })
@ApiQuery({
name: 'page',
required: false,
description: 'Page number for pagination',
type: Number
})
@ApiQuery({
name: 'limit',
required: false,
description: 'Number of items per page',
type: Number
})
@ApiOkResponse({
description: 'Group responses validation result',
schema: {
type: 'object',
properties: {
testTakersFound: { type: 'boolean' },
groupsWithResponses: {
type: 'array',
items: {
type: 'object',
properties: {
group: { type: 'string' },
hasResponse: { type: 'boolean' }
}
}
},
allGroupsHaveResponses: { type: 'boolean' },
total: { type: 'number' },
page: { type: 'number' },
limit: { type: 'number' }
}
}
})
async validateGroupResponses(
@Param('workspace_id') workspace_id: number,
@Query('page') page: number = 1,
@Query('limit') limit: number = 10
): Promise<{
testTakersFound: boolean;
groupsWithResponses: { group: string; hasResponse: boolean }[];
allGroupsHaveResponses: boolean;
total: number;
page: number;
limit: number;
}> {
return this.workspaceFilesService.validateGroupResponses(workspace_id, page, limit);
}

@Get(':workspace_id/files/validate-response-status')
@ApiTags('test files validation')
@UseGuards(JwtAuthGuard, WorkspaceGuard)
@ApiOperation({ summary: 'Validate response status', description: 'Validates if response status is one of the valid values (VALUE_CHANGED, NOT_REACHED, DISPLAYED, UNSET, PARTLY_DISPLAYED)' })
@ApiParam({ name: 'workspace_id', type: Number, description: 'ID of the workspace' })
@ApiQuery({
name: 'page',
required: false,
description: 'Page number for pagination',
type: Number
})
@ApiQuery({
name: 'limit',
required: false,
description: 'Number of items per page',
type: Number
})
@ApiOkResponse({
description: 'Response status validation result',
schema: {
type: 'object',
properties: {
data: { type: 'array', items: { $ref: '#/components/schemas/InvalidVariableDto' } },
total: { type: 'number' },
page: { type: 'number' },
limit: { type: 'number' }
}
}
})
async validateResponseStatus(
@Param('workspace_id') workspace_id: number,
@Query('page') page: number = 1,
@Query('limit') limit: number = 10
): Promise<{ data: InvalidVariableDto[]; total: number; page: number; limit: number }> {
return this.workspaceFilesService.validateResponseStatus(workspace_id, page, limit);
}

@Get(':workspace_id/files/validate-variables')
@ApiTags('test files validation')
@UseGuards(JwtAuthGuard, WorkspaceGuard)
@ApiOperation({ summary: 'Validate variables', description: 'Validates if variables in responses are defined in Unit-XMLs' })
@ApiParam({ name: 'workspace_id', type: Number, description: 'ID of the workspace' })
@ApiQuery({
name: 'page',
required: false,
description: 'Page number for pagination',
type: Number
})
@ApiQuery({
name: 'limit',
required: false,
description: 'Number of items per page',
type: Number
})
@ApiOkResponse({
description: 'Variables validation result',
schema: {
type: 'object',
properties: {
data: { type: 'array', items: { $ref: '#/components/schemas/InvalidVariableDto' } },
total: { type: 'number' },
page: { type: 'number' },
limit: { type: 'number' }
}
}
})
async validateVariables(
@Param('workspace_id') workspace_id: number,
@Query('page') page: number = 1,
@Query('limit') limit: number = 10
): Promise<{ data: InvalidVariableDto[]; total: number; page: number; limit: number }> {
return this.workspaceFilesService.validateVariables(workspace_id, page, limit);
}

@Get(':workspace_id/files/validate-variable-types')
@ApiTags('test files validation')
@UseGuards(JwtAuthGuard, WorkspaceGuard)
@ApiOperation({ summary: 'Validate variable types', description: 'Validates if variable values match their defined types in Unit-XMLs' })
@ApiParam({ name: 'workspace_id', type: Number, description: 'ID of the workspace' })
@ApiQuery({
name: 'page',
required: false,
description: 'Page number for pagination',
type: Number
})
@ApiQuery({
name: 'limit',
required: false,
description: 'Number of items per page',
type: Number
})
@ApiOkResponse({
description: 'Variable types validation result',
schema: {
type: 'object',
properties: {
data: { type: 'array', items: { $ref: '#/components/schemas/InvalidVariableDto' } },
total: { type: 'number' },
page: { type: 'number' },
limit: { type: 'number' }
}
}
})

async validateVariableTypes(
@Param('workspace_id') workspace_id: number,
@Query('page') page: number = 1,
@Query('limit') limit: number = 10
): Promise<{ data: InvalidVariableDto[]; total: number; page: number; limit: number }> {
return this.workspaceFilesService.validateVariableTypes(workspace_id, page, limit);
}

@Delete(':workspace_id/files/invalid-responses')
@ApiTags('test files validation')
@UseGuards(JwtAuthGuard, WorkspaceGuard)
@ApiOperation({ summary: 'Delete invalid responses', description: 'Deletes invalid responses from the database' })
@ApiParam({ name: 'workspace_id', type: Number, description: 'ID of the workspace' })
@ApiQuery({ name: 'responseIds', type: String, description: 'Comma-separated list of response IDs to delete' })
@ApiOkResponse({
description: 'Number of deleted responses',
type: Number
})
async deleteInvalidResponses(
@Param('workspace_id') workspace_id: number,
@Query('responseIds') responseIds: string): Promise<number> {
const ids = responseIds.split(',').map(id => parseInt(id, 10));
return this.workspaceFilesService.deleteInvalidResponses(workspace_id, ids);
}
}
Loading