From affbb71d0e2a8fe754b7b5dcdd6a25e57e6f0b40 Mon Sep 17 00:00:00 2001 From: "bedran.sezer" <bedran.sezer@etu.hesge.ch> Date: Fri, 21 Jun 2024 01:13:25 +0200 Subject: [PATCH] add functions (list/filter/info) for assignment) --- .../commander/assignment/AssignmentCommand.ts | 4 + .../subcommands/AssignmentInfoCommand.ts | 87 +++++++ .../subcommands/AssignmentListCommand.ts | 233 ++++++++++++++++++ NodeApp/src/managers/DojoBackendManager.ts | 13 + 4 files changed, 337 insertions(+) create mode 100644 NodeApp/src/commander/assignment/subcommands/AssignmentInfoCommand.ts create mode 100644 NodeApp/src/commander/assignment/subcommands/AssignmentListCommand.ts diff --git a/NodeApp/src/commander/assignment/AssignmentCommand.ts b/NodeApp/src/commander/assignment/AssignmentCommand.ts index 8d36acc..8f39363 100644 --- a/NodeApp/src/commander/assignment/AssignmentCommand.ts +++ b/NodeApp/src/commander/assignment/AssignmentCommand.ts @@ -5,6 +5,8 @@ import AssignmentUnpublishCommand from './subcommands/AssignmentUnpublishComman import AssignmentCheckCommand from './subcommands/AssignmentCheckCommand.js'; import AssignmentRunCommand from './subcommands/AssignmentRunCommand.js'; import AssignmentCorrectionCommand from './subcommands/correction/AssignmentCorrectionCommand.js'; +import AssignmentListCommand from "./subcommands/AssignmentListCommand"; +import AssignmentInfoCommand from "./subcommands/AssignmentInfoCommand"; class AssignmentCommand extends CommanderCommand { @@ -22,6 +24,8 @@ class AssignmentCommand extends CommanderCommand { AssignmentPublishCommand.registerOnCommand(this.command); AssignmentUnpublishCommand.registerOnCommand(this.command); AssignmentCorrectionCommand.registerOnCommand(this.command); + AssignmentListCommand.registerOnCommand(this.command); + AssignmentInfoCommand.registerOnCommand(this.command); } protected async commandAction(): Promise<void> { diff --git a/NodeApp/src/commander/assignment/subcommands/AssignmentInfoCommand.ts b/NodeApp/src/commander/assignment/subcommands/AssignmentInfoCommand.ts new file mode 100644 index 0000000..7a9057a --- /dev/null +++ b/NodeApp/src/commander/assignment/subcommands/AssignmentInfoCommand.ts @@ -0,0 +1,87 @@ +import CommanderCommand from "../../CommanderCommand"; +import chalk from "chalk"; +import ora from "ora"; +import DojoBackendManager from "../../../managers/DojoBackendManager"; +import AccessesHelper from "../../../helpers/AccessesHelper"; +import Assignment from "../../../sharedByClients/models/Assignment"; +import Exercise from "../../../sharedByClients/models/Exercise"; +import inquirer from "inquirer"; + +class AssignmentInfoCommand extends CommanderCommand { + protected commandName: string = 'info'; + + protected defineCommand(): void { + this.command + .description('Get details of a assignment by name or GitLab link') + .argument('"<assignmentNameOrUrl>"', 'The name or URL of the assignment') + .action(this.commandAction.bind(this)); + } + + protected async commandAction(assignmentNameOrUrl: string): Promise<void> { + console.log(chalk.cyan('Please wait while we retrieve your assignments...')); + + // Check access + if (!await AccessesHelper.checkStudent()) { + return; + } + + // Fetch user's assignments + const userAssignments: Assignment[] | undefined = await DojoBackendManager.getUserAssignments(); + + if (!userAssignments || userAssignments.length === 0) { + ora().info('You have no assignments yet.'); + return; + } + + // Find the assignment by name or GitLab link + const assignment = userAssignments.find(assignment => + assignment.name === assignmentNameOrUrl || assignment.gitlabLink === assignmentNameOrUrl + ); + + if (!assignment) { + if (assignmentNameOrUrl.startsWith("http")) { + ora().info(`No assignment found with this URL : ${assignmentNameOrUrl}`); + } else { + ora().info(`No assignment found with this name : ${assignmentNameOrUrl}`); + } + return; + } + + // Fetch all exercises + const allExercises: Exercise[] | undefined = await DojoBackendManager.getUserExercises(); + + if (!allExercises) { + ora().info('Unable to retrieve exercises.'); + return; + } + + // Filter exercises by assignment name + const assignmentExercises = allExercises.filter(exercise => exercise.assignmentName === assignment.name); + + // Display the assignment details + await this.displayAssignmentDetails(assignment, assignmentExercises.length); + } + + private async displayAssignmentDetails(assignment: Assignment, exerciseCount: number): Promise<void> { + ora().info(`Detail of Assignment`); + console.log(chalk.magenta(' - Assignment Name:'), assignment.name); + console.log(chalk.magenta(' - GitLab ID:'), assignment.gitlabId); + console.log(chalk.magenta(' - GitLab Link:'), chalk.blue.underline(assignment.gitlabLink)); + console.log(chalk.magenta(' - GitLab Last Info Date:'), assignment.gitlabLastInfoDate); + console.log(chalk.magenta(' - Number of Exercises Created from this assignment :'), exerciseCount); + + // Fetch assignment members + const assignmentMembers = await DojoBackendManager.getAssignmentMembers(assignment.name); + + if (assignmentMembers && assignmentMembers.length > 0) { + ora().info('Assignment Members:'); + assignmentMembers.forEach(member => { + console.log(chalk.magenta(` - ${member.id} ${member.name}`)); + }); + } else { + ora().info('No members found for this assignment.'); + } + } +} + +export default new AssignmentInfoCommand(); diff --git a/NodeApp/src/commander/assignment/subcommands/AssignmentListCommand.ts b/NodeApp/src/commander/assignment/subcommands/AssignmentListCommand.ts new file mode 100644 index 0000000..cf9a33b --- /dev/null +++ b/NodeApp/src/commander/assignment/subcommands/AssignmentListCommand.ts @@ -0,0 +1,233 @@ +import CommanderCommand from '../../CommanderCommand'; +import chalk from 'chalk'; +import ora from 'ora'; +import DojoBackendManager from '../../../managers/DojoBackendManager'; +import AccessesHelper from '../../../helpers/AccessesHelper'; +import Assignment from '../../../sharedByClients/models/Assignment'; +import inquirer from 'inquirer'; +import Table from 'cli-table3'; + +import Fuse from 'fuse.js'; +import User from '../../../sharedByClients/models/User'; + +class AssignmentListCommand extends CommanderCommand { + protected commandName: string = 'list'; + + protected defineCommand(): void { + this.command + .description('list your assignments') + .action(this.commandAction.bind(this)); + } + + protected async commandAction(): Promise<void> { + console.log(chalk.cyan('Please wait while we retrieve your assignments...')); + + // Check access + if (!await AccessesHelper.checkStudent()) { + return; + } + + // Fetch user's assignment + const userAssignments: Assignment[] | undefined = await DojoBackendManager.getUserAssignments(); + + if (!userAssignments || userAssignments.length === 0) { + ora().info('You have no assignments yet.'); + return; + } + + // Display the list of assignment + this.displayAssignmentList(userAssignments); + + // Ask the user for further actions + await this.askUserForActions(userAssignments); + } + + private async askUserForActions(assignments: Assignment[]): Promise<void> { + const { action } = await inquirer.prompt([ + { + type: 'list', + name: 'action', + message: 'Que souhaitez-vous faire ?', + choices: [ + { name: 'Voir les détails d\'énoncé', value: 'details'}, + { name: 'Filter les énoncés', value: 'filter' }, + { name: 'Exit', value: 'exit' }, + ], + }, + ]); + + if (action === 'details') { + await this.selectAssignmentForDetails(assignments); + } else if (action === 'filter') { + await this.filterAssignments(assignments); + } else { + ora().info('No further actions selected.'); + } + } + + private async selectAssignmentForDetails(assignments:Assignment[]): Promise<void> { + const { selectedAssignment } = await inquirer.prompt([{ + type: 'list', + name: 'selectedAssignment', + message: 'Selectionnez un énoncé :', + choices: [ + ...assignments.map(assignment => ({ + name: assignment.name, + value: assignment.name, + })), + { name: 'Exit', value: 'exit' }, + ], + }]); + + if (selectedAssignment === 'exit') { + ora().info('Pas de détails requis: détails dispo avec la commande `dojo assignment info <id>`.'); + return; + } + + const selected = assignments.find(as => as.name === selectedAssignment); + if (selected) { + await this.displayAssignmentDetails(selected); + } else { + ora().info('Invalid selection. No assignment details to show.'); + } + } + + private async filterAssignments(assignments: Assignment[]): Promise<void> { + const { filterType } = await inquirer.prompt([ + { + type: 'list', + name: 'filterType', + message: 'Comment souhaitez-vous filtrer les énoncés ?', + choices: [ + { name: 'Par saisie texte', value: 'fuzzy' }, + { name: 'Par professeurs', value: 'professor' }, + { name: 'Exit', value: 'exit' }, + ], + }, + ]); + + if (filterType === 'fuzzy') { + await this.fuzzySearchAssignments(assignments); + } else if (filterType === 'professor') { + await this.filterByProfessor(assignments); + } else { + ora().info('No filtering selected.'); + } + } + + private async fuzzySearchAssignments(assignments: Assignment[]): Promise<void> { + const { searchQuery } = await inquirer.prompt([ + { + type: 'input', + name: 'searchQuery', + message: 'Entrez le nom de l\'énoncé (laissez vide pour la liste complète) :', + }, + ]); + + if (!searchQuery) { + this.displayAssignmentList(assignments); + return; + } + + const fuse = new Fuse(assignments, { + keys: ['name'], + threshold: 0.5, + distance: 150, + }); + + const searchResults = fuse.search(searchQuery).map(result => result.item); + + if (searchResults.length === 0) { + ora().info('Aucun énoncé trouvé correspondant à votre recherche.'); + return; + } + + if (searchResults.length === 1) { + // Display details and members for the single matching assignment + const singleAssignment = searchResults[0]; + this.displayAssignmentDetails(singleAssignment); + } else { + // Display only assignment names and info about viewing details + ora().info(' Plusieurs énoncés trouvés correspondant à votre recherche :'); + const assignmentsName = searchResults.map(assignment => assignment.name); + console.log(' -', assignmentsName.join('\n - ')); + + ora().info('Les détails sont disponibles avec la commande : `dojo assignment info <id>`.'); + } + } + + private async filterByProfessor(assignments: Assignment[]): Promise<void> { + + const professors: User[] | undefined = await DojoBackendManager.getProfessors(); + + if (!professors || professors.length === 0) { + ora().info('No professors found.'); + return; + } + + const professorChoices = professors.map(professor => ({ + name: `${professor.gitlabUsername}`, + value: professor + })); + + const { selectedProfessor } = await inquirer.prompt([ + { + type: 'list', + name: 'selectedProfessor', + message: 'Selectionnez un professeur:', + choices: professorChoices + } + ]); + + console.log(`Selected professor: ${selectedProfessor.gitlabUsername}`); + ora().info('Filter by professor is not yet implemented.'); + } + + private displayAssignmentList(assignments: Assignment[]): void { + const headers = ['Assignment Name', 'GitLab Link']; + + // Calculate the maximum width for each column + const maxWidths = headers.map(header => header.length); + + assignments.forEach(assignment=> { + maxWidths[0] = Math.max(maxWidths[0], assignment.name.length); + maxWidths[1] = Math.max(maxWidths[1], assignment.gitlabLink.length); + }); + + const table = new Table({ + head: headers, + }); + + assignments.forEach((assignment) => { + table.push([ + assignment.name, + assignment.gitlabLink, + ]); + }); + + ora().info('Your Assignment:'); + console.log(table.toString()); + } + + private async displayAssignmentDetails(assignment: Assignment): Promise<void> { + ora().info(`Detail of Assignment`); + console.log(chalk.magenta(' - Assignment Name:'), assignment.name) + console.log(chalk.magenta(' - GitLab ID:'), assignment.gitlabId); + console.log(chalk.magenta(' - GitLab Link:'), chalk.blue.underline(assignment.gitlabLink)); + console.log(chalk.magenta(' - GitLab Last Info Date:'), assignment.gitlabLastInfoDate); + + // Fetch assignment members + const assignmentMember = await DojoBackendManager.getAssignmentMembers(assignment.name); + + if (assignmentMember && assignmentMember.length > 0) { + ora().info('Assignments Members:'); + assignmentMember.forEach(member => { + console.log(chalk.magenta(` - ${member.id} ${member.name}`)); + }); + } else { + ora().info('No members found for this assignment.'); + } + } +} + +export default new AssignmentListCommand(); \ No newline at end of file diff --git a/NodeApp/src/managers/DojoBackendManager.ts b/NodeApp/src/managers/DojoBackendManager.ts index c48da6e..f6d2447 100644 --- a/NodeApp/src/managers/DojoBackendManager.ts +++ b/NodeApp/src/managers/DojoBackendManager.ts @@ -261,6 +261,15 @@ class DojoBackendManager { } } + public async getUserAssignments(): Promise<Assignment[]> { + try { + const response = await axios.get(DojoBackendHelper.getApiUrl(ApiRoute.ASSIGNMENT_LIST)); + return response.data as Assignment[] + } catch (error) { + console.error('Error fetching user assignments:', error); + return []; + } + } public async getExerciseDetail(exerciseId: string): Promise<Exercise | undefined> { try { @@ -292,6 +301,10 @@ class DojoBackendManager { return (await axios.get<DojoBackendResponse<Array<User>>>(DojoBackendHelper.getApiUrl(ApiRoute.EXERCISE_MEMBER).replace('{{exerciseId}}', exerciseId))).data.data; } + public async getAssignmentMembers(assignmentName: string): Promise<Array<User>> { + return (await axios.get<DojoBackendResponse<Array<User>>>(DojoBackendHelper.getApiUrl(ApiRoute.ASSIGNMENT_MEMBER).replace('{{assignmentNameOrUrl}}', assignmentName))).data.data; + } + public async getExerciseResults(exerciseId: string): Promise<Result[]> { -- GitLab