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