diff --git a/apps/backend/src/app/admin/workspace/workspace-files.controller.ts b/apps/backend/src/app/admin/workspace/workspace-files.controller.ts index 42b14f4c7..854c866ea 100644 --- a/apps/backend/src/app/admin/workspace/workspace-files.controller.ts +++ b/apps/backend/src/app/admin/workspace/workspace-files.controller.ts @@ -73,7 +73,8 @@ export class WorkspaceFilesController { data: { type: 'array', items: { $ref: '#/components/schemas/FilesDto' } }, total: { type: 'number' }, page: { type: 'number' }, - limit: { type: 'number' } + limit: { type: 'number' }, + fileTypes: { type: 'array', items: { type: 'string' } } } } }) @@ -91,21 +92,22 @@ export class WorkspaceFilesController { @Query('fileType') fileType?: string, @Query('fileSize') fileSize?: string, @Query('searchText') searchText?: string - ): Promise<{ data: FilesDto[]; total: number; page: number; limit: number }> { + ): Promise<{ data: FilesDto[]; total: number; page: number; limit: number; fileTypes: string[] }> { if (!workspace_id || workspace_id <= 0) { throw new BadRequestException( 'Invalid workspace ID. Please provide a valid ID.' ); } try { - const [files, total] = await this.workspaceFilesService.findFiles(workspace_id, { + const [files, total, fileTypes] = await this.workspaceFilesService.findFiles(workspace_id, { page, limit, fileType, fileSize, searchText }); return { data: files, total, page, - limit + limit, + fileTypes }; } catch (error) { throw new BadRequestException( diff --git a/apps/backend/src/app/database/entities/file_upload.entity.ts b/apps/backend/src/app/database/entities/file_upload.entity.ts index 4141a625f..bf29f305b 100755 --- a/apps/backend/src/app/database/entities/file_upload.entity.ts +++ b/apps/backend/src/app/database/entities/file_upload.entity.ts @@ -3,7 +3,7 @@ import { } from 'typeorm'; @Entity() -@Unique('file_upload_id', ['file_id']) +@Unique('file_upload_id', ['file_id', 'workspace_id']) class FileUpload { @PrimaryColumn({ type: 'integer' }) id: number; diff --git a/apps/backend/src/app/database/services/workspace-coding.service.ts b/apps/backend/src/app/database/services/workspace-coding.service.ts index 5f3c7fc11..4105d178d 100644 --- a/apps/backend/src/app/database/services/workspace-coding.service.ts +++ b/apps/backend/src/app/database/services/workspace-coding.service.ts @@ -11,7 +11,6 @@ import { Unit } from '../entities/unit.entity'; import { Booklet } from '../entities/booklet.entity'; import { ResponseEntity } from '../entities/response.entity'; import { CodingStatistics } from './shared-types'; -import { prepareDefinition } from '../../utils/voud/transform'; import { extractVariableLocation } from '../../utils/voud/extractVariableLocation'; @Injectable() @@ -350,7 +349,7 @@ export class WorkspaceCodingService { const bookletInfo = booklet?.bookletinfo; const loginName = person?.login || ''; const loginCode = person?.code || ''; - const loginGroup = person.group || ''; + //const loginGroup = person.group || ''; const bookletId = bookletInfo?.name || ''; const unitKey = unit?.name || ''; const unitAlias = unit?.alias || ''; @@ -386,7 +385,7 @@ export class WorkspaceCodingService { this.logger.warn(`VOUD file not found for unit ${unitKey}`); } - const url = `${server}/#/replay/${loginName}@${loginCode}@${loginGroup}/${unitKey}/${variablePage}/${variableAnchor}?auth=${authToken}`; + const url = `${server}/#/replay/${loginName}@${loginCode}@${bookletId}/${unitKey}/${variablePage}/${variableAnchor}?auth=${authToken}`; return { unit_key: unitKey, @@ -431,7 +430,7 @@ export class WorkspaceCodingService { const bookletInfo = booklet?.bookletinfo; const loginName = person?.login || ''; const loginCode = person?.code || ''; - const loginGroup = person.group || ''; + // const loginGroup = person.group || ''; const bookletId = bookletInfo?.name || ''; const unitKey = unit?.name || ''; const unitAlias = unit?.alias || ''; @@ -468,7 +467,7 @@ export class WorkspaceCodingService { this.logger.warn(`VOUD file not found for unit ${unitKey}`); } - const url = `${server}/#/replay/${loginName}@${loginCode}@${loginGroup}/${unitKey}/${variablePage}/${variableAnchor}?auth=${authToken}`; + const url = `${server}/#/replay/${loginName}@${loginCode}@${bookletId}/${unitKey}/${variablePage}/${variableAnchor}?auth=${authToken}`; return { unit_key: unitKey, unit_alias: unitAlias, diff --git a/apps/backend/src/app/database/services/workspace-files.service.ts b/apps/backend/src/app/database/services/workspace-files.service.ts index d313c91a6..c226d746e 100644 --- a/apps/backend/src/app/database/services/workspace-files.service.ts +++ b/apps/backend/src/app/database/services/workspace-files.service.ts @@ -65,10 +65,28 @@ export class WorkspaceFilesService { private fileUploadRepository: Repository ) {} + async findAllFileTypes(workspaceId: number): Promise { + this.logger.log(`Fetching all file types for workspace: ${workspaceId}`); + + try { + const result = await this.fileUploadRepository + .createQueryBuilder('file') + .select('DISTINCT file.file_type', 'file_type') + .where('file.workspace_id = :workspaceId', { workspaceId }) + .andWhere('file.file_type IS NOT NULL') + .getRawMany(); + + return result.map(item => item.file_type).sort(); + } catch (error) { + this.logger.error(`Error fetching file types for workspace ${workspaceId}: ${error.message}`, error.stack); + return []; + } + } + async findFiles( workspaceId: number, options?: { page: number; limit: number; fileType?: string; fileSize?: string; searchText?: string } - ): Promise<[FilesDto[], number]> { + ): Promise<[FilesDto[], number, string[]]> { this.logger.log(`Fetching test files for workspace: ${workspaceId}`); const { page = 1, limit = 20, fileType, fileSize, searchText @@ -89,6 +107,7 @@ export class WorkspaceFilesService { // fileSize-Filter: z.B. '0-10KB', '10KB-100KB', '100KB-1MB', '1MB-10MB', '10MB+' const KB = 1024; const MB = 1024 * KB; + // eslint-disable-next-line default-case switch (fileSize) { case '0-10KB': qb = qb.andWhere('file.file_size < :max', { max: 10 * KB }); @@ -123,7 +142,11 @@ export class WorkspaceFilesService { const [files, total] = await qb.getManyAndCount(); this.logger.log(`Found ${files.length} files (page ${validPage}, limit ${validLimit}, total ${total}).`); - return [files, total]; + + // Get all file types for this workspace + const fileTypes = await this.findAllFileTypes(workspaceId); + + return [files, total, fileTypes]; } async deleteTestFiles(workspace_id: number, fileIds: string[]): Promise { @@ -468,7 +491,7 @@ export class WorkspaceFilesService { file_size: file.size, data: file.buffer.toString(), file_id: resolvedFileId - }, ['file_id']); + }, ['file_id', 'workspace_id']); } catch (error) { this.logger.error(`Error processing XML file: ${error.message}`); throw error; @@ -485,7 +508,7 @@ export class WorkspaceFilesService { file_size: file.size, file_id: resourceFileId, data: file.buffer.toString() - }, ['file_id']); + }, ['file_id', 'workspace_id']); } private async handleOctetStreamFile(workspaceId: number, file: FileIo): Promise { @@ -800,7 +823,7 @@ export class WorkspaceFilesService { async testCenterImport(entries: Record[]): Promise { try { const registry = this.fileUploadRepository.create(entries); - await this.fileUploadRepository.upsert(registry, ['file_id']); + await this.fileUploadRepository.upsert(registry, ['file_id', 'workspace_id']); return true; } catch (error) { this.logger.error('Error during test center import', error); @@ -811,20 +834,9 @@ export class WorkspaceFilesService { private static getPlayerId(file: FileIo): string { try { const playerCode = file.buffer.toString(); - const playerContent = cheerio.load(playerCode); - - // Search for JSON+LD