From 5be27bb6ebc8cdefa5740325535026dd27b86331 Mon Sep 17 00:00:00 2001
From: "bedran.sezer" <bedran.sezer@etu.hesge.ch>
Date: Mon, 17 Jun 2024 14:23:04 +0200
Subject: [PATCH] add delete

---
 .gitmodules                                   |   8 +-
 .idea/.gitignore                              |   5 +
 .idea/dojocli.iml                             |  12 +
 .idea/modules.xml                             |   8 +
 .idea/vcs.xml                                 |   6 +
 NodeApp/package-lock.json                     |   9 +
 NodeApp/package.json                          |   4 +-
 .../src/commander/exercise/ExerciseCommand.ts |  10 +
 .../subcommands/ExerciseDeleteCommand.ts      |  33 +++
 .../subcommands/ExerciseDetailCommand.ts      | 104 ++++++++
 .../subcommands/ExerciseListCommand.ts        | 229 ++++++++++++++++++
 .../subcommands/ExerciseMemberCommand.ts      |  37 +++
 .../subcommands/ExerciseResultCommand.ts      |  92 +++++++
 NodeApp/src/managers/DojoBackendManager.ts    |  55 ++++-
 NodeApp/src/sharedByClients                   |   2 +-
 shared                                        |   1 -
 sharedByClients                               |   1 -
 17 files changed, 603 insertions(+), 13 deletions(-)
 create mode 100644 .idea/.gitignore
 create mode 100644 .idea/dojocli.iml
 create mode 100644 .idea/modules.xml
 create mode 100644 .idea/vcs.xml
 create mode 100644 NodeApp/src/commander/exercise/subcommands/ExerciseDeleteCommand.ts
 create mode 100644 NodeApp/src/commander/exercise/subcommands/ExerciseDetailCommand.ts
 create mode 100644 NodeApp/src/commander/exercise/subcommands/ExerciseListCommand.ts
 create mode 100644 NodeApp/src/commander/exercise/subcommands/ExerciseMemberCommand.ts
 create mode 100644 NodeApp/src/commander/exercise/subcommands/ExerciseResultCommand.ts
 delete mode 160000 shared
 delete mode 160000 sharedByClients

diff --git a/.gitmodules b/.gitmodules
index 94297cb..139e244 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -6,10 +6,4 @@
 	url = https://gitedu.hesge.ch/dojo_project/projects/shared/nodesharedcode
 [submodule "NodeApp/src/sharedByClients"]
 	path = NodeApp/src/sharedByClients
-	url = https://gitedu.hesge.ch/dojo_project/projects/shared/nodeclientsharedcode
-[submodule "shared"]
-	path = shared
-	url = ../../shared/nodesharedcode.git
-[submodule "sharedByClients"]
-	path = sharedByClients
-	url = ../../shared/nodeclientsharedcode.git
+	url = https://gitedu.hesge.ch/dojo_project/projects/shared/nodeclientsharedcode
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..b58b603
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,5 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/.idea/dojocli.iml b/.idea/dojocli.iml
new file mode 100644
index 0000000..24643cc
--- /dev/null
+++ b/.idea/dojocli.iml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$">
+      <excludeFolder url="file://$MODULE_DIR$/.tmp" />
+      <excludeFolder url="file://$MODULE_DIR$/temp" />
+      <excludeFolder url="file://$MODULE_DIR$/tmp" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..4ab3051
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/dojocli.iml" filepath="$PROJECT_DIR$/.idea/dojocli.iml" />
+    </modules>
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="" vcs="Git" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/NodeApp/package-lock.json b/NodeApp/package-lock.json
index 2ae9954..c551479 100644
--- a/NodeApp/package-lock.json
+++ b/NodeApp/package-lock.json
@@ -22,6 +22,7 @@
                 "commander": "^12.1.0",
                 "form-data": "^4.0.0",
                 "fs-extra": "^11.2.0",
+                "fuse.js": "^7.0.0",
                 "http-status-codes": "^2.3.0",
                 "inquirer": "^8.2.6",
                 "json5": "^2.2.3",
@@ -3552,6 +3553,14 @@
                 "url": "https://github.com/sponsors/ljharb"
             }
         },
+        "node_modules/fuse.js": {
+            "version": "7.0.0",
+            "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.0.0.tgz",
+            "integrity": "sha512-14F4hBIxqKvD4Zz/XjDc3y94mNZN6pRv3U13Udo0lNLCWRBUsrMv2xwcF/y/Z5sV6+FQW+/ow68cHpm4sunt8Q==",
+            "engines": {
+                "node": ">=10"
+            }
+        },
         "node_modules/genversion": {
             "version": "3.2.0",
             "resolved": "https://registry.npmjs.org/genversion/-/genversion-3.2.0.tgz",
diff --git a/NodeApp/package.json b/NodeApp/package.json
index 5cc93c6..f7692dd 100644
--- a/NodeApp/package.json
+++ b/NodeApp/package.json
@@ -8,9 +8,9 @@
     "bin"            : {
         "dojo": "./dist/app.js"
     },
-    "pkg"            : {
+    "pkg": {
         "scripts": [],
-        "assets" : [
+        "assets": [
             "node_modules/axios/dist/node/axios.cjs",
             ".env",
             "assets/**/*"
diff --git a/NodeApp/src/commander/exercise/ExerciseCommand.ts b/NodeApp/src/commander/exercise/ExerciseCommand.ts
index 8089092..7437868 100644
--- a/NodeApp/src/commander/exercise/ExerciseCommand.ts
+++ b/NodeApp/src/commander/exercise/ExerciseCommand.ts
@@ -2,6 +2,11 @@ import CommanderCommand          from '../CommanderCommand.js';
 import ExerciseCreateCommand     from './subcommands/ExerciseCreateCommand.js';
 import ExerciseRunCommand        from './subcommands/ExerciseRunCommand.js';
 import ExerciseCorrectionCommand from './subcommands/ExerciseCorrectionCommand.js';
+import ExerciseListCommand       from './subcommands/ExerciseListCommand';
+import ExerciseDetailCommand     from './subcommands/ExerciseDetailCommand';
+import ExerciseDeleteCommand     from './subcommands/ExerciseDeleteCommand';
+import ExerciseMemberCommand     from './subcommands/ExerciseMemberCommand';
+import ExerciseResultCommand     from './subcommands/ExerciseResultCommand';
 
 
 class ExerciseCommand extends CommanderCommand {
@@ -15,6 +20,11 @@ class ExerciseCommand extends CommanderCommand {
     protected defineSubCommands() {
         ExerciseCreateCommand.registerOnCommand(this.command);
         ExerciseRunCommand.registerOnCommand(this.command);
+        ExerciseListCommand.registerOnCommand(this.command);
+        ExerciseDetailCommand.registerOnCommand(this.command);
+        ExerciseDeleteCommand.registerOnCommand(this.command);
+        ExerciseMemberCommand.registerOnCommand(this.command);
+        ExerciseResultCommand.registerOnCommand(this.command);
         ExerciseCorrectionCommand.registerOnCommand(this.command);
     }
 
diff --git a/NodeApp/src/commander/exercise/subcommands/ExerciseDeleteCommand.ts b/NodeApp/src/commander/exercise/subcommands/ExerciseDeleteCommand.ts
new file mode 100644
index 0000000..a5fd25b
--- /dev/null
+++ b/NodeApp/src/commander/exercise/subcommands/ExerciseDeleteCommand.ts
@@ -0,0 +1,33 @@
+import CommanderCommand from '../../CommanderCommand';
+import chalk from 'chalk';
+import ora from 'ora';
+import DojoBackendManager from '../../../managers/DojoBackendManager';
+import AccessesHelper from '../../../helpers/AccessesHelper';
+
+class ExerciseDeleteCommand extends CommanderCommand {
+    protected commandName: string = 'delete';
+
+    protected defineCommand(): void {
+        this.command
+            .description('delete an exercise')
+            .argument('[exerciseId]', 'ID of the exercise to delete')
+            .action(this.commandAction.bind(this));
+    }
+
+    protected async commandAction(exerciseId: string): Promise<void> {
+        console.log(chalk.cyan('Please wait while we delete your exercise...'));
+
+        if (!await AccessesHelper.checkStudent()) {
+            return;
+        }
+        try {
+            const spinner = ora(`Deleting exercise  ${exerciseId}`).start();
+            await DojoBackendManager.deleteExercise(exerciseId);
+            spinner.succeed(`Exercise with ID ${exerciseId} has been successfully deleted.`);
+        } catch (error) {
+            ora().fail(`Failed to delete exercise with ID ${exerciseId}`);
+        }
+    }
+}
+
+export default new ExerciseDeleteCommand();
diff --git a/NodeApp/src/commander/exercise/subcommands/ExerciseDetailCommand.ts b/NodeApp/src/commander/exercise/subcommands/ExerciseDetailCommand.ts
new file mode 100644
index 0000000..a2a00e2
--- /dev/null
+++ b/NodeApp/src/commander/exercise/subcommands/ExerciseDetailCommand.ts
@@ -0,0 +1,104 @@
+// ExerciseListCommand.ts
+import CommanderCommand from '../../CommanderCommand';
+import chalk from 'chalk';
+import ora from 'ora';
+import DojoBackendManager from '../../../managers/DojoBackendManager';
+import AccessesHelper from '../../../helpers/AccessesHelper';
+import Exercise from '../../../sharedByClients/models/Exercise';
+//import Result from '../../../sharedByClients/models/Result';
+import Table from 'cli-table3';
+
+
+
+
+
+class ExerciseListCommand extends CommanderCommand {
+    protected commandName: string = 'info';
+
+    protected defineCommand(): void {
+        this.command
+            .description('detail of an exercise')
+            .argument('[exerciseId]', 'display detail of a specific exercise by id')
+            .action(this.commandAction.bind(this));
+    }
+
+
+    protected async commandAction(exerciseId?: string): Promise<void> {
+        console.log(chalk.cyan('Please wait while we retrieve your exercise...'));
+
+        // Check access
+        if (!await AccessesHelper.checkStudent()) {
+            return;
+        }
+
+        const userExercises: Exercise[] | undefined = await DojoBackendManager.getUserExercises();
+
+        if (!userExercises || userExercises.length === 0) {
+            ora().info('You have no exercise yet.');
+            return;
+        }
+
+        //console.log(userExercises);
+
+        if (exerciseId) {
+            const exercise = userExercises.find(exercise => exercise.id === exerciseId);
+
+            if (!exercise) {
+                ora().info(`No exercise found with ID :  ${exerciseId}.`);
+                return;
+            }
+
+            ora().info(`Detail of Exercise with  ID :  ${exerciseId}:`);
+            console.log(chalk.magenta('  - Exercise Name:'), exercise.name);
+            console.log(chalk.magenta('  - Assignment Name:'), exercise.assignmentName);
+            console.log(chalk.magenta('  - GitLab ID:'), exercise.gitlabId);
+            console.log(chalk.magenta('  - GitLab Link:'), chalk.blue.underline(exercise.gitlabLink));
+            console.log(chalk.magenta('  - GitLab Last Info Date:'), exercise.gitlabLastInfoDate);
+            return;
+        } else {
+            ora().info('No exercise with this ID.');
+        }
+
+
+        // Display list of all exercises
+        //this.displayExerciseList(userExercises);
+    }
+
+    private displayExerciseList(exercises: Exercise[]): void {
+        const table = new Table({
+        head: ['#', 'Exercise Name']
+    });
+
+    exercises.forEach((exercise, index) => {
+        // Add 1 to index to convert zero-based index to one-based index for display
+        table.push([index + 1, exercise.name]);
+    });
+
+    ora().info('Your exercises:');
+    console.log(table.toString());
+    }
+
+}
+
+export default new ExerciseListCommand();
+
+
+
+// // Display test results
+        //     if (results && results.length > 0) {
+        //         console.log(chalk.magenta('  - Test Results:'));
+        //         const successfulTests = results.filter(result => result.status === 'success').length;
+        //         const failedTests = results.filter(result => result.status === 'failure').length;
+        //         console.log(chalk.magenta('    Successful Tests:'), successfulTests);
+        //         console.log(chalk.magenta('    Failed Tests:'), failedTests);
+        //         console.log(chalk.magenta('    Successful Tests List:'));
+        //         results.filter(result => result.status === 'success').forEach(result => {
+        //             console.log(chalk.magenta(`      - ${result.testName}`));
+        //         });
+        //         console.log(chalk.magenta('    Failed Tests List:'));
+        //         results.filter(result => result.status === 'failure').forEach(result => {
+        //             console.log(chalk.magenta(`      - ${result.testName}`));
+        //         });
+        //     } else {
+        //         console.log(chalk.yellow('  - No test results found.'));
+        //     }
\ No newline at end of file
diff --git a/NodeApp/src/commander/exercise/subcommands/ExerciseListCommand.ts b/NodeApp/src/commander/exercise/subcommands/ExerciseListCommand.ts
new file mode 100644
index 0000000..c51bd3c
--- /dev/null
+++ b/NodeApp/src/commander/exercise/subcommands/ExerciseListCommand.ts
@@ -0,0 +1,229 @@
+// ExerciseListCommand.ts
+import CommanderCommand from '../../CommanderCommand';
+import chalk from 'chalk';
+import ora from 'ora';
+import DojoBackendManager from '../../../managers/DojoBackendManager';
+import AccessesHelper from '../../../helpers/AccessesHelper';
+import Exercise from '../../../sharedByClients/models/Exercise';
+import inquirer from 'inquirer';
+import Table from 'cli-table3';
+
+import Fuse from 'fuse.js';
+import User from '../../../sharedByClients/models/User';
+
+class ExerciseListCommand extends CommanderCommand {
+    protected commandName: string = 'list';
+
+    protected defineCommand(): void {
+        this.command
+            .description('list your exercises')
+            .action(this.commandAction.bind(this));
+    }
+
+    protected async commandAction(): Promise<void> {
+        console.log(chalk.cyan('Please wait while we retrieve your exercises...'));
+
+        // Check access
+        if (!await AccessesHelper.checkStudent()) {
+            return;
+        }
+
+        // Fetch user's exercises
+        const userExercises: Exercise[] | undefined = await DojoBackendManager.getUserExercises();
+
+        if (!userExercises || userExercises.length === 0) {
+            ora().info('You have no exercises yet.');
+            return;
+        }
+
+        // Display the list of exercises
+        this.displayExerciseList(userExercises);
+
+        // Ask the user for further actions
+        await this.askUserForActions(userExercises);
+    }
+
+    private async askUserForActions(exercises: Exercise[]): Promise<void> {
+        const { action } = await inquirer.prompt([
+            {
+                type: 'list',
+                name: 'action',
+                message: 'Que souhaitez-vous faire ?',
+                choices: [
+                    { name: 'Voir les détails d\'exercice', value: 'details'},
+                    { name: 'Filter les exercises', value: 'filter' },
+                    { name: 'Exit', value: 'exit' },
+                ],
+            },
+        ]);
+
+        if (action === 'details') {
+            await this.selectExerciseForDetails(exercises);
+        } else if (action === 'filter') {
+            await this.filterExercises(exercises);
+        } else {
+            ora().info('No further actions selected.');
+        }
+    }
+
+    private async selectExerciseForDetails(exercises: Exercise[]): Promise<void> {
+        const { selectedExercise } = await inquirer.prompt([{
+            type: 'list',
+            name: 'selectedExercise',
+            message: 'Selectionner un exercice :',
+            choices: [
+                ...exercises.map(exercise => ({
+                    name: exercise.name,
+                    value: exercise.id,
+                })),
+                { name: 'Exit', value: 'exit' },
+            ],
+        }]);
+
+        if (selectedExercise === 'exit') {
+            ora().info('Pas de détails requis: détails dispo  avec la commande `dojo exercise info <id>`.');
+            return;
+        }
+
+        const selected = exercises.find(ex => ex.id === selectedExercise);
+        if (selected) {
+            await this.displayExerciseDetails(selected);
+        } else {
+            ora().info('Invalid selection. No exercise details to show.');
+        }
+    }
+
+    private async filterExercises(exercises: Exercise[]): Promise<void> {
+        const { filterType } = await inquirer.prompt([
+            {
+                type: 'list',
+                name: 'filterType',
+                message: 'Comment souhaitez-vous filtrer les exercices ?',
+                choices: [
+                    { name: 'Par saisie texte', value: 'fuzzy' },
+                    { name: 'Par professeurs', value: 'professor' },
+                    { name: 'Exit', value: 'exit' },
+                ],
+            },
+        ]);
+
+        if (filterType === 'fuzzy') {
+            await this.fuzzySearchExercises(exercises);
+        } else if (filterType === 'professor') {
+            await this.filterByProfessor(exercises);
+        } else {
+            ora().info('No filtering selected.');
+        }
+    }
+
+     private async fuzzySearchExercises(exercises: Exercise[]): Promise<void> {
+        const { searchQuery } = await inquirer.prompt([
+            {
+                type: 'input',
+                name: 'searchQuery',
+                message: 'Entrez le nom de l\'exercice (laisser vide pour la liste complète) :',
+            },
+        ]);
+
+        if (!searchQuery) {
+            this.displayExerciseList(exercises);
+            return;
+        }
+
+        const fuse = new Fuse(exercises, {
+            keys: ['name'],
+            threshold: 0.5,
+            distance: 150,
+        });
+
+        const filteredExercises = fuse.search(searchQuery).map(result => result.item);
+
+        if (filteredExercises.length === 0) {
+            ora().info('Aucun exercice trouvé correspondant à votre recherche.');
+            return;
+        }
+
+        this.displayExerciseList(filteredExercises);
+
+        // Affichage des détails de chaque exercice trouvé
+        for (const exercise of filteredExercises) {
+            await this.displayExerciseDetails(exercise);
+        }
+    }
+
+    private async filterByProfessor(exercises: Exercise[]): 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 // Use the professor object as the value
+        }));
+
+        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 displayExerciseList(exercises: Exercise[]): void {
+        const headers = ['Exercise Name', 'GitLab Link'];
+
+        // Calculate the maximum width for each column
+        const maxWidths = headers.map(header => header.length);
+
+        exercises.forEach(exercise => {
+            maxWidths[0] = Math.max(maxWidths[0], exercise.name.length);
+            maxWidths[1] = Math.max(maxWidths[1], exercise.gitlabLink.length);
+        });
+
+        const table = new Table({
+            head: headers,
+        });
+
+        exercises.forEach((exercise) => {
+            table.push([
+                exercise.name,
+                exercise.gitlabLink,
+            ]);
+        });
+
+        ora().info('Your exercises:');
+        console.log(table.toString());
+    }
+
+    private async displayExerciseDetails(exercise: Exercise): Promise<void> {
+        ora().info(`Detail of Exercise with ID: ${exercise.id}`);
+        console.log(chalk.magenta('  - Exercise Name:'), exercise.name);
+        console.log(chalk.magenta('  - Assignment Name:'), exercise.assignmentName);
+        console.log(chalk.magenta('  - GitLab ID:'), exercise.gitlabId);
+        console.log(chalk.magenta('  - GitLab Link:'), chalk.blue.underline(exercise.gitlabLink));
+        console.log(chalk.magenta('  - GitLab Last Info Date:'), exercise.gitlabLastInfoDate);
+
+        // Fetch exercise members
+        const exerciseMembers = await DojoBackendManager.getExerciseMembers(exercise.id);
+
+        if (exerciseMembers && exerciseMembers.length > 0) {
+            ora().info('Exercise Members:');
+            exerciseMembers.forEach(member => {
+                console.log(chalk.magenta(`  - ${member.id} ${member.name}`));
+            });
+        } else {
+            ora().info('No members found for this exercise.');
+        }
+    }
+}
+
+export default new ExerciseListCommand();
\ No newline at end of file
diff --git a/NodeApp/src/commander/exercise/subcommands/ExerciseMemberCommand.ts b/NodeApp/src/commander/exercise/subcommands/ExerciseMemberCommand.ts
new file mode 100644
index 0000000..a4445fd
--- /dev/null
+++ b/NodeApp/src/commander/exercise/subcommands/ExerciseMemberCommand.ts
@@ -0,0 +1,37 @@
+import CommanderCommand from '../../CommanderCommand';
+import chalk from 'chalk';
+import ora from 'ora';
+import DojoBackendManager from '../../../managers/DojoBackendManager';
+import AccessesHelper from '../../../helpers/AccessesHelper';
+
+
+class ExerciseMembersCommand extends CommanderCommand {
+    protected commandName: string = 'members';
+
+    protected defineCommand(): void {
+            this.command
+                .description('detail of an exercise')
+                .argument('[exerciseId]', 'display detail of a specific exercise by id')
+                .action(this.commandAction.bind(this));
+    }
+      
+    protected async commandAction(exerciseId: string): Promise<void> {
+        console.log(chalk.cyan('Please wait while we fetch the members of the exercise...'));
+
+        if (!await AccessesHelper.checkStudent()) {
+            return;
+        }
+        try {
+            const spinner = ora(`Fetching members for exercise ${exerciseId}`).start();
+            const members = await DojoBackendManager.getExerciseMembers(exerciseId);
+            spinner.succeed(`Members of exercise with ID ${exerciseId}:`);
+            members.forEach(member => {
+                console.log(`ID: ${member.id}, Name: ${member.name}`);
+            });
+        } catch (error) {
+            ora().fail(`Failed to fetch members for exercise with ID ${exerciseId}`);
+        }
+    }
+}
+
+export default new ExerciseMembersCommand();
diff --git a/NodeApp/src/commander/exercise/subcommands/ExerciseResultCommand.ts b/NodeApp/src/commander/exercise/subcommands/ExerciseResultCommand.ts
new file mode 100644
index 0000000..9c2657a
--- /dev/null
+++ b/NodeApp/src/commander/exercise/subcommands/ExerciseResultCommand.ts
@@ -0,0 +1,92 @@
+import CommanderCommand from '../../CommanderCommand';
+import chalk from 'chalk';
+import ora from 'ora';
+import DojoBackendManager from '../../../managers/DojoBackendManager';
+import Result from '../../../sharedByClients/models/Result';
+
+
+class ExerciseResultCommand extends CommanderCommand {
+    protected commandName: string = 'result';
+
+    protected defineCommand(): void {
+        this.command
+            .description('results of an exercise')
+            .argument('[idOrLink]', 'display results of a specific exercise by id')
+            .action(this.commandAction.bind(this));
+    }
+
+    protected async commandAction(idOrLink: string): Promise<void> {
+    const spinner = ora('Fetching exercise results...').start();
+
+    try {
+        const exerciseId = this.extractExerciseId(idOrLink);
+        //console.log('Exercise ID:', exerciseId);
+        spinner.info(`Fetching results for exercise with ID: ${exerciseId}`);
+        const results = await DojoBackendManager.getExerciseResults(exerciseId);
+
+        if (!results) {
+            spinner.info('No results found for this exercise.');
+            spinner.succeed('Exercise results fetched successfully.');
+            return;
+        }
+
+        if (results.length === 0) {
+            spinner.info('No results found for this exercise.');
+        } else {
+            this.displayResults(results);
+            spinner.succeed('Exercise results fetched successfully.');
+        }
+    } catch (error) {
+        spinner.fail('Error fetching exercise results.');
+        console.error(error);
+    }
+}
+
+
+    private extractExerciseId(idOrLink: string): string {
+        if (idOrLink.length <= 36) {
+        return idOrLink;
+    } else {
+        const lastUnderscoreIndex = idOrLink.lastIndexOf('_');
+        //console.log('Last underscore index:', lastUnderscoreIndex);
+        if (lastUnderscoreIndex !== -1) { // -1 = pas de underscore trouvé
+            return idOrLink.substring(lastUnderscoreIndex + 1); // Extrait la sous-chaîne après le dernier underscore dans idOrLink
+        } else {
+            return '';
+        }
+    }
+
+    }
+
+    private displayResults(results: Result[]): void {
+        if (!results || results.length === 0) {
+            console.log('No results to display.');
+            return;
+        }
+
+        results.forEach(result => {
+        console.log(chalk.magenta('Résultats de l`exercice :'));
+        console.log(`  - Date et heure : ${result.dateTime}`);
+        console.log(`  - Succès : ${result.success ? chalk.green('Oui') : chalk.red('Non')}`);
+        console.log('  - Détails des résultats :');
+        console.log(`    - Tests réussis : ${result.results.successfulTests}`);
+        console.log(`    - Tests échoués : ${result.results.failedTests}`);
+        console.log('    - Liste des tests réussis :');
+        if (Array.isArray(result.results.successfulTestsList)) {
+            result.results.successfulTestsList.forEach((test: string) => {
+                console.log(`      - ${test} ${chalk.green('\u2713')}`);
+            });
+        }
+        console.log('    - Liste des tests échoués :');
+        if (Array.isArray(result.results.failedTestsList)) {
+            result.results.failedTestsList.forEach((test: string) => {
+                console.log(`      - ${test} ${chalk.red('\u2717')}`);
+            });
+        }
+
+        console.log('-----------------------------------');
+    });
+    }
+}
+
+export default new ExerciseResultCommand();
\ No newline at end of file
diff --git a/NodeApp/src/managers/DojoBackendManager.ts b/NodeApp/src/managers/DojoBackendManager.ts
index 414945f..b571dfe 100644
--- a/NodeApp/src/managers/DojoBackendManager.ts
+++ b/NodeApp/src/managers/DojoBackendManager.ts
@@ -13,6 +13,7 @@ import DojoBackendHelper     from '../sharedByClients/helpers/Dojo/DojoBackendHe
 import GitlabPipelineStatus  from '../shared/types/Gitlab/GitlabPipelineStatus.js';
 import Tag                   from '../sharedByClients/models/Tag';
 import TagProposal           from '../sharedByClients/models/TagProposal';
+import Result                from '../sharedByClients/models/Result';
 
 
 class DojoBackendManager {
@@ -84,7 +85,6 @@ class DojoBackendManager {
         }
     }
 
-
     public async login(gitlabTokens: GitlabToken): Promise<User | undefined> {
         try {
             return (await axios.post<DojoBackendResponse<User>>(DojoBackendHelper.getApiUrl(ApiRoute.LOGIN), {
@@ -365,6 +365,59 @@ class DojoBackendManager {
             return false;
         }
     }
+
+    public async getUserExercises(): Promise<Exercise[] | undefined> {
+        try {
+            const response = await axios.get<DojoBackendResponse<Exercise[]>>(this.getApiUrl(ApiRoute.EXERCISE_LIST));
+            return response.data.data;
+        } catch ( error ) {
+            console.error('Error fetching user exercises:', error);
+            return undefined;
+        }
+    }
+
+    public async getExerciseDetail(exerciseId: string): Promise<Exercise | undefined> {
+        try {
+
+            const response = await axios.get<Exercise>(this.getApiUrl(ApiRoute.EXERCISE_DETAIL).replace('{{exerciseId}}', String(exerciseId)));
+            return response.data;
+        } catch ( error ) {
+            console.error('Error fetching exercise details:', error);
+            return undefined;
+        }
+    }
+
+
+    public async deleteExercise(exerciseId: string): Promise<Exercise> {
+        return (await axios.patch<DojoBackendResponse<Exercise>>(this.getApiUrl(ApiRoute.EXERCISE_DELETE).replace('{{exerciseId}}', exerciseId))).data.data;
+    }
+
+    public async getExerciseMembers(exerciseId: string): Promise<Array<User>> {
+        return (await axios.get<DojoBackendResponse<Array<User>>>(this.getApiUrl(ApiRoute.EXERCISE_MEMBER).replace('{{exerciseId}}', exerciseId))).data.data;
+    }
+
+
+    public async getExerciseResults(exerciseId: string): Promise<Result[]> {
+        try {
+            const response = await axios.get(this.getApiUrl(ApiRoute.EXERCISE_RESULTS).replace('{{idOrLink}}', exerciseId));
+            //console.log('response.data:', response.data);
+            return response.data as Result[];
+        } catch ( error ) {
+            console.error('Error fetching exercise results:', error);
+            return [];
+        }
+    }
+
+    public async getProfessors(): Promise<User[] | undefined> {
+        try {
+            const response = await axios.get<DojoBackendResponse<User[]>>(this.getApiUrl(ApiRoute.TEACHERS));
+            const users = response.data.data;
+            return users;
+        } catch ( error ) {
+            console.error('Error fetching professors:', error);
+            return undefined;
+        }
+    }
 }
 
 
diff --git a/NodeApp/src/sharedByClients b/NodeApp/src/sharedByClients
index 8c87edb..8670731 160000
--- a/NodeApp/src/sharedByClients
+++ b/NodeApp/src/sharedByClients
@@ -1 +1 @@
-Subproject commit 8c87edbbc5734d6b85d1e52063e8d815807277c0
+Subproject commit 86707310b276fd641f794e8c9f410c42bdb58206
diff --git a/shared b/shared
deleted file mode 160000
index 9e3f29d..0000000
--- a/shared
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 9e3f29d2f313ef96944a199da0db39f1827c496a
diff --git a/sharedByClients b/sharedByClients
deleted file mode 160000
index 4377a70..0000000
--- a/sharedByClients
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 4377a70c3c731a5426fc0d4c88c9e8858f0c5361
-- 
GitLab