Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d4de018
Modify the `WorkspaceCodingService` to process test persons in batches
jurei733 Jul 16, 2025
e698f03
Run validation tasks in background
jurei733 Jul 16, 2025
e867a63
Fix scrolling in files validation
jurei733 Jul 16, 2025
ecfbe16
Check files that each unit is used minimum once in a booklet
jurei733 Jul 16, 2025
3a322ea
Check test files that all units for each booklet are available
jurei733 Jul 16, 2025
5dd70c5
Check test files that each unit has a player defined
jurei733 Jul 16, 2025
73a2c28
Provide dummy test taker file for minimum configuration
jurei733 Jul 16, 2025
3ee4178
Check if booklet is used by minimum one testtaker
jurei733 Jul 16, 2025
4da128d
Add warning if not each booklet of testtaker is available.
jurei733 Jul 16, 2025
b587f99
Create dummy booklet additionally if only units in workspace
jurei733 Jul 16, 2025
f3307de
Check test taker groups for certain mode and filter possibility
jurei733 Jul 17, 2025
1069dd1
Highlight the aspect section in replay where the variable anchor is f…
jurei733 Jul 17, 2025
6b1e143
Check for double test taker entries
jurei733 Jul 17, 2025
8527412
Validate for duplicate responses
jurei733 Jul 18, 2025
45d0f8a
Implement unit notes
jurei733 Jul 20, 2025
b441fcf
Add a booklet replay option
jurei733 Jul 20, 2025
af96089
Add a booklet info button
jurei733 Jul 20, 2025
e8a0045
Increase Postgres Shared buffers
jurei733 Jul 20, 2025
1bc84b1
Set version to 0.10.0
jurei733 Jul 20, 2025
0b36dbf
Upgrade nest js to version 11
jurei733 Jul 20, 2025
f8e7d3a
Bump the npm_and_yarn group across 1 directory with 3 updates
dependabot[bot] Jul 20, 2025
3a87e1d
Merge pull request #188 from iqb-berlin/dependabot/npm_and_yarn/npm_a…
jurei733 Jul 20, 2025
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
12 changes: 12 additions & 0 deletions api-dto/booklet-info/booklet-config-item.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ApiProperty } from '@nestjs/swagger';

/**
* DTO for booklet configuration item
*/
export class BookletConfigItemDto {
@ApiProperty({ description: 'Configuration key', example: 'loading_mode' })
key!: string;

@ApiProperty({ description: 'Configuration value', example: 'LAZY' })
value!: string;
}
10 changes: 10 additions & 0 deletions api-dto/booklet-info/booklet-config.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ApiProperty } from '@nestjs/swagger';
import { BookletConfigItemDto } from './booklet-config-item.dto';

/**
* DTO for booklet configuration collection
*/
export class BookletConfigDto {
@ApiProperty({ description: 'List of configuration items', type: [BookletConfigItemDto] })
items!: BookletConfigItemDto[];
}
26 changes: 26 additions & 0 deletions api-dto/booklet-info/booklet-info.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { ApiProperty } from '@nestjs/swagger';
import { BookletMetadataDto } from './booklet-metadata.dto';
import { BookletUnitDto } from './booklet-unit.dto';
import { BookletRestrictionDto } from './booklet-restriction.dto';
import { BookletConfigDto } from './booklet-config.dto';
import { BookletTestletDto } from './booklet-testlet.dto';

export class BookletInfoDto {
@ApiProperty({ description: 'Booklet metadata' })
metadata!: BookletMetadataDto;

@ApiProperty({ description: 'Booklet configuration', required: false })
config?: BookletConfigDto;

@ApiProperty({ description: 'List of units in the booklet', type: [BookletUnitDto] })
units!: BookletUnitDto[];

@ApiProperty({ description: 'List of testlets in the booklet', type: [BookletTestletDto], required: false })
testlets?: BookletTestletDto[];

@ApiProperty({ description: 'List of restrictions for the booklet', type: [BookletRestrictionDto] })
restrictions!: BookletRestrictionDto[];

@ApiProperty({ description: 'Raw XML of the booklet', required: false })
rawXml?: string;
}
15 changes: 15 additions & 0 deletions api-dto/booklet-info/booklet-metadata.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ApiProperty } from '@nestjs/swagger';

/**
* DTO for booklet metadata
*/
export class BookletMetadataDto {
@ApiProperty({ description: 'Booklet ID', example: 'BOOKLET123' })
id!: string;

@ApiProperty({ description: 'Booklet label', example: 'Math Test 2023' })
label?: string;

@ApiProperty({ description: 'Booklet description', example: 'Mathematics assessment for grade 10' })
description?: string;
}
12 changes: 12 additions & 0 deletions api-dto/booklet-info/booklet-restriction.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ApiProperty } from '@nestjs/swagger';

/**
* DTO for booklet restriction
*/
export class BookletRestrictionDto {
@ApiProperty({ description: 'Restriction type', example: 'timeMax' })
type!: string;

@ApiProperty({ description: 'Restriction value', example: '60' })
value!: string;
}
20 changes: 20 additions & 0 deletions api-dto/booklet-info/booklet-testlet.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ApiProperty } from '@nestjs/swagger';
import { BookletUnitDto } from './booklet-unit.dto';
import { BookletRestrictionDto } from './booklet-restriction.dto';

/**
* DTO for booklet testlet
*/
export class BookletTestletDto {
@ApiProperty({ description: 'Testlet ID', example: 'TESTLET123' })
id!: string;

@ApiProperty({ description: 'Testlet label', example: 'Math Section' })
label?: string;

@ApiProperty({ description: 'List of units in the testlet', type: [BookletUnitDto] })
units!: BookletUnitDto[];

@ApiProperty({ description: 'List of restrictions for the testlet', type: [BookletRestrictionDto] })
restrictions?: BookletRestrictionDto[];
}
18 changes: 18 additions & 0 deletions api-dto/booklet-info/booklet-unit.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { ApiProperty } from '@nestjs/swagger';

/**
* DTO for booklet unit
*/
export class BookletUnitDto {
@ApiProperty({ description: 'Unit ID', example: 'UNIT123' })
id!: string;

@ApiProperty({ description: 'Unit label', example: 'Algebra Problem 1' })
label?: string;

@ApiProperty({ description: 'Unit alias', example: 'M1' })
alias?: string;

@ApiProperty({ description: 'Unit position in booklet', example: 1 })
position!: number;
}
20 changes: 20 additions & 0 deletions api-dto/files/duplicate-response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export interface DuplicateResponseDto {
unitName: string;
unitId: number;
variableId: string;
bookletName: string;
testTakerLogin: string;
duplicates: {
responseId: number;
value: string;
status: string;
timestamp?: number;
}[];
}

export interface DuplicateResponsesResultDto {
data: DuplicateResponseDto[];
total: number;
page: number;
limit: number;
}
23 changes: 23 additions & 0 deletions api-dto/files/file-validation-result.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,36 @@ type FileStatus = {
type DataValidation = {
complete: boolean;
missing: string[];
missingUnitsPerBooklet?: { booklet: string; missingUnits: string[] }[];
unitsWithoutPlayer?: string[];
unusedBooklets?: string[];
files: FileStatus[];
};

export type FilteredTestTaker = {
testTaker: string;
mode: string;
login: string;
};

export type DuplicateTestTaker = {
login: string;
occurrences: {
testTaker: string;
mode: string;
}[];
};

export class FileValidationResultDto {
@ApiProperty({ type: Boolean, description: 'Indicates whether test takers were found' })
testTakersFound!: boolean;

@ApiProperty({ type: [Object], description: 'Array of filtered test takers with specific modes' })
filteredTestTakers?: FilteredTestTaker[];

@ApiProperty({ type: [Object], description: 'Array of duplicate test takers found across files' })
duplicateTestTakers?: DuplicateTestTaker[];

@ApiProperty({ type: [Object], description: 'Array of validation results for each test taker' })
validationResults!: {
testTaker: string;
Expand Down
16 changes: 13 additions & 3 deletions apps/backend/src/app/admin/admin.module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Module } from '@nestjs/common';
import { HttpModule } from '@nestjs/axios';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersController } from './users/users.controller';
import { DatabaseModule } from '../database/database.module';
import { AuthModule } from '../auth/auth.module';
Expand All @@ -17,12 +18,17 @@ import { ResourcePackageController } from './resource-packages/resource-package.
import { JournalController } from './workspace/journal.controller';
import { VariableAnalysisController } from './variable-analysis/variable-analysis.controller';
import { JobsController } from './jobs/jobs.controller';
import { ValidationTaskController } from './workspace/validation-task.controller';
import { BookletInfoController } from './workspace/booklet-info.controller';
import { BookletInfoService } from '../database/services/booklet-info.service';
import FileUpload from '../database/entities/file_upload.entity';

@Module({
imports: [
DatabaseModule,
AuthModule,
HttpModule
HttpModule,
TypeOrmModule.forFeature([FileUpload])
],
controllers: [
UsersController,
Expand All @@ -39,8 +45,12 @@ import { JobsController } from './jobs/jobs.controller';
ResourcePackageController,
JournalController,
VariableAnalysisController,
JobsController
JobsController,
ValidationTaskController,
BookletInfoController
],
providers: []
providers: [
BookletInfoService
]
})
export class AdminModule {}
31 changes: 31 additions & 0 deletions apps/backend/src/app/admin/unit-notes/unit-notes.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,37 @@ export class UnitNotesController {
}
}

@Post('units/notes')
@UseGuards(JwtAuthGuard, WorkspaceGuard)
@ApiBearerAuth()
@ApiOperation({
summary: 'Get all notes for multiple units',
description: 'Retrieves all notes for the specified units'
})
@ApiParam({
name: 'workspace_id',
type: Number,
required: true,
description: 'The ID of the workspace'
})
@ApiOkResponse({
description: 'The notes have been successfully retrieved.',
type: Object
})
@ApiBadRequestResponse({
description: 'Invalid input data.'
})
async findAllByUnitIds(
@WorkspaceId() workspaceId: number,
@Body() { unitIds }: { unitIds: number[] }
): Promise<{ [unitId: number]: UnitNoteDto[] }> {
try {
return await this.unitNoteService.findAllByUnitIds(unitIds);
} catch (error) {
throw new BadRequestException(`Failed to retrieve notes: ${error.message}`);
}
}

@Get(':id')
@UseGuards(JwtAuthGuard, WorkspaceGuard)
@ApiBearerAuth()
Expand Down
38 changes: 38 additions & 0 deletions apps/backend/src/app/admin/workspace/booklet-info.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {
Controller,
Get,
Param,
UseGuards
} from '@nestjs/common';
import {
ApiOperation,
ApiParam,
ApiResponse,
ApiTags
} from '@nestjs/swagger';
import { JwtAuthGuard } from '../../auth/jwt-auth.guard';
import { BookletInfoService } from '../../database/services/booklet-info.service';
import { BookletInfoDto } from '../../../../../../api-dto/booklet-info/booklet-info.dto';

@ApiTags('Booklet Info')
@Controller('admin/workspace/:workspaceId/booklet')
@UseGuards(JwtAuthGuard)
export class BookletInfoController {
constructor(private readonly bookletInfoService: BookletInfoService) {}

@Get(':bookletId/info')
@ApiOperation({ summary: 'Get booklet info from XML' })
@ApiParam({ name: 'workspaceId', type: Number })
@ApiParam({ name: 'bookletId', type: String })
@ApiResponse({
status: 200,
description: 'Booklet info retrieved successfully',
type: BookletInfoDto
})
async getBookletInfo(
@Param('workspaceId') workspaceId: number,
@Param('bookletId') bookletId: string
): Promise<BookletInfoDto> {
return this.bookletInfoService.getBookletInfo(workspaceId, bookletId);
}
}
32 changes: 32 additions & 0 deletions apps/backend/src/app/admin/workspace/dto/validation-task.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { ValidationTask } from '../../../database/entities/validation-task.entity';

export class ValidationTaskDto {
id: number;
workspace_id: number;
validation_type: 'variables' | 'variableTypes' | 'responseStatus' | 'testTakers' | 'groupResponses' | 'deleteResponses' | 'deleteAllResponses' | 'duplicateResponses';
status: 'pending' | 'processing' | 'completed' | 'failed';
progress?: number;
error?: string;
page?: number;
limit?: number;
created_at: Date;
updated_at: Date;

/**
* Convert a ValidationTask entity to a ValidationTaskDto
*/
static fromEntity(entity: ValidationTask): ValidationTaskDto {
const dto = new ValidationTaskDto();
dto.id = entity.id;
dto.workspace_id = entity.workspace_id;
dto.validation_type = entity.validation_type;
dto.status = entity.status as 'pending' | 'processing' | 'completed' | 'failed';
dto.progress = entity.progress;
dto.error = entity.error;
dto.page = entity.page;
dto.limit = entity.limit;
dto.created_at = entity.created_at;
dto.updated_at = entity.updated_at;
return dto;
}
}
Loading
Loading