From 621ab8e16316ee136b6d3872a556a77971b4da11 Mon Sep 17 00:00:00 2001 From: "bedran.sezer" <bedran.sezer@etu.hesge.ch> Date: Thu, 20 Jun 2024 23:30:32 +0200 Subject: [PATCH] update fuzzy matching --- .idea/.gitignore | 5 + .idea/dojocli.iml | 12 + .idea/inspectionProfiles/Project_Default.xml | 6 + .idea/modules.xml | 8 + .idea/vcs.xml | 9 + NodeApp/.env.vault | 8 +- .../src/commander/exercise/ExerciseCommand.ts | 2 + .../subcommands/ExerciseListCommand.ts | 235 ++++++++++++++++++ NodeApp/src/managers/DojoBackendManager.ts | 66 +++++ 9 files changed, 347 insertions(+), 4 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/dojocli.iml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 NodeApp/src/commander/exercise/subcommands/ExerciseListCommand.ts 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/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..03d9549 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ +<component name="InspectionProjectProfileManager"> + <profile version="1.0"> + <option name="myName" value="Project Default" /> + <inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" /> + </profile> +</component> \ 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..cdbd4ac --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="VcsDirectoryMappings"> + <mapping directory="" vcs="Git" /> + <mapping directory="$PROJECT_DIR$/NodeApp/.idea/jetbrainsConfiguration" vcs="Git" /> + <mapping directory="$PROJECT_DIR$/NodeApp/src/shared" vcs="Git" /> + <mapping directory="$PROJECT_DIR$/NodeApp/src/sharedByClients" vcs="Git" /> + </component> +</project> \ No newline at end of file diff --git a/NodeApp/.env.vault b/NodeApp/.env.vault index 85a3476..103f5eb 100644 --- a/NodeApp/.env.vault +++ b/NodeApp/.env.vault @@ -1,14 +1,14 @@ #/-------------------.env.vault---------------------/ #/ cloud-agnostic vaulting standard / -#/ [how it works](https://dotenvx.com/env-vault) / +#/ [how it works](https://dotenv.org/env-vault) / #/--------------------------------------------------/ # development -DOTENV_VAULT_DEVELOPMENT="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" +DOTENV_VAULT_DEVELOPMENT="xWZfuXiO1nWw4qDf/vY90xPpLhb7l2cyOYK7v7K2WPOjlSuJBL74nWHaphDeslFUpJYiIjNe2QOn7z5nqn6DWw4PqYkjEPTjYGLfXmig3QoQtehYyYDErFH9RYRzCSE34NZzk2GP+F6vNGKJ0fA+ua4sTPfzPuhuoSC6Oz6TXsUwmDz7PpsrRDSsf1Y8e2GRHUiyYYb5W5lLKtFn2wCRNMU41nHgOsZip4/gmoz/6wD3vpJptLnMXMWUO7EZ87cyWXKZxVHCP19WmW1Fncdb4bo297VL1vH4b2yYcGwtgBAlsrMHhEzJH3QcXZucDdvIsN5A39CuIreQPM5szUonUJZIXcwXga8cmvuioszNO4zvrnG+BpMxOmLxa5XYYjUepfuSdP260zeMcLnAg0Mbylg6PlcaoA8fjKHiJZeAt2KMH5aL5dMLi5VMlleF2Wq0XbklZwyflfxzjULYJ6XNnrUJNea63Su1vzj7gPTA05eAfYnlKlNF8zc76qlY/G5Wigz0InOim3omo1JrLB4uBWCSkJvM6dxnB15DJfHjWHSJETI0desojXremJ11cnqabH6miC/hKH6A9YOEax4zdMIrRTMFa24xFuHjHqbTe/1/G6URW1kKKmyi9r8hNBKEyAYq1PYnf2BiGBYRj5KMybkYwqB+C0xLucAXYIXgOamw91vuN3SVpuLQ2adz7KLiZbe5xiXZALXhzXj08j195A7sZUoz5yWfqZcVE9o0EkGzX6xAGqVADg1mbgw9dRbm+Ux9u0/qAMzpTbK93lRtC94ZBKenGtJc+Sdc6rYmUKj/+3mmIGYGQjX6VAV2ApoC+yJxyG1WBy/Ae0BeoGtuevuCbYjM55KHWy5cFh3s71I3eJVfcJxf7+bi3VQudOOLE8ZhK42Oa6tY/Aqgr2oyjcfBvA+B0R/wJBy+ZIOKtZgtcH9mIl8Srw3FgGEPch/F4r3ndooM1TuDD/yVyFK0crstir8iD+nMExj9ll8fi2TmcAQ5CC0/0QteCEb9qeVCwa0mO0xIk8IVxC9hVlrInk+yw1A4uZLYwe9ppIjlSGf0zLqIJPiOY8mPTpCucgrYV+BhybIhUKGIdCbXu+JR/LZ7babQAUXfeA86kkKhcNc2HVEpx8ms7kvju3d06rTMdxCz/isEpmzVobm0ZEzXzBe+VBk71YP6liVDrGO0YXPz/ew000fZOyhcZbEryCjZO9WjES5w3uPiTo6MGLmuKfjRDgrJQuQ05wYrDdH7YnXKY0jrzSibSEkuywdXjVgq4YgTMnLKtsc8AIVlB/CzI4Xq8w5gPPz0kZVo/xmThziwjkk0TIlRKpFjlito/QBKquZ38YmXhkOBbSGpCsEOtQfThtbnArKhojhQLcSOruAwIE8wQBJDr1yW+/WtqeWi80E9C/QI91zMKNM/DsD/JSZmkZAXwyV0R7/IVoZlX2JQgTGxD0VjGI1Tsr3mGfU2zUOBif9Ff6tcYhWBfUrVIv1InTa905tpZ/1DObYE9a3+stFERtGgKICdq801fSwKjvXq4ijFgJEwD0sGAj6E52X995qTxftyMcCkByWmTiTltZfHeNOYBRODhl2y15rHvsBh2XjERiuN1NyVUhytinHCqx6+OMRvQ0TOLplSuR0XEBL9v3Iz3zvkJ6K6t12FXD5TzjlIKUfUVgHUHcZVZo1HuGxvuS4Ymo5qAJzgv8g+yZfEml+AMDkZfBCP6KO9f8wi6X+X4Z54dDDxrXNyyBwmv58a9GMWyFDiQNTH8caCs4T2h2uzB56yJkjL2DDMeZtw9WL5mFQ0kUxeaO7J1iLsccbJ58yRHsowHY4Y/wSNaeNEHKNCRFyIEC7nHSQawqmZZXmCX1Dgs83V5sLM5pBjHnWQ2xI=" # production -DOTENV_VAULT_PRODUCTION="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" +DOTENV_VAULT_PRODUCTION="geUVFVOsvd0vRIIfeu1i6pKoAr3/2nwB63oO4gxe/ug3GnFBEh+Rf8I7DwM4qcD10PBqaMV7tJiYVIBqAUviHrQSGEJw5aK4Le4ndXf/1yPpsRf6YdMNfL/oU5I2aMMXlCCGTylbgon67Vrw2SgmZbCD1vkr9wmdJqATANcNSfNbJL+mf790rZyUXYC4piUIfdGDnjJpeSTozfpZeTh3yEp0pRbjRsU6NlXIPH/mDVFJkWMT31jZJ1kyePu6b1qWWh2uZRwWaaOOTclpKGnLRaT/N3Z+M1OJbB1Kjp0uUoLIz4VytTGcYRjS8wbfzBnl4wg9HSPR49UKShX5SNtQ9ov/7vmRpYH59SNuwV55S6Bv5pgOUVjBgdl+4tzXuh7Z8FD2867a2k7vXBpXoOBgi6yujlSmEoul3XMa5a3zyWHB+kz0GwEtlyZyAFR5xXyIWdURkXw4RRLLhHaI5y6LGKPCFHyFPdEUX4uRroFB261+ft5IT51ZwTujeymC6qkC2KlsrpViyySTjE4LynLzitIsLP5I7PiZ6tH227BfzcwmUdHlSuiHIQMWPKl2oFLyhEkfWgUxCRUfUxYMgCRjPkrXf7zPhvhgbtOOTBLlZsMBQxFoM7kXECzhnnyqp6JVQxfOMAGPTcgLQP3HAqVLVfAYzRpfoTROxuoZ6nuikIdR8rO4BAasH2/6Z0hWEGKUMsnhDHb8l3xDNB3IAH00y+t1WM0Yre49s3RrtTz+I8K3nZ1hCCT6UqfDxRkefltXngdd47qNU49FpWS2MKitUapLvlqTccAxYrCUmAkl/giDMNrRaLFbWSeiA8tyOEhdp8eqVz5ygLalkecdZTr3x3qwFbLam++MqLph1JXeFWzBoekLyJUrtWEYNBXb/g+eQlfHKEbd0AbbYsSaqVZz9R8hjqcryq/O+pMETx7F8DV7l8oR1xMaFeDBc+rJPLXka5plc91mdstU3E0FgviScUvaE1dclEuPGW3M/1MXR+oxIZ81dl4j1uUYcSUueKv7QsOS8jFuJhQQXzNyBrFtJCb8jV8V9/0vXKUN+Zj9Bfdn0IuUxXN8VfaoSNBOA2fTssEAPSvCBOJPSQsL7YsSye/1koMCifYy+c2RDSDxxD72bfsij9U1srjJgGJAqRI2WetUjH9HgqESJ+oPQouKhHwEinnsNVSweoLLN0nD02RSjm3iIeCy/5R0B/23yALZ4YWTxdS4kJs22mue0cUyGY6on3eCmwF5d+7H9/r737sbLYODBLn6eArXvHAvIS72mtvveTiviXhzU8c0ntu562zpDDMTn8useCWHgsEukRm2eumpMiXSMCiFSotV4T+GFNSAkm3J6mGAzZFp1QOgZHnImjrERsLaKRweQblNT2ZYxaZNbCNMlrj248EIIIbSPH+/4wgUyl5tbhnyPYO58Z0V1kLOvofuzIIVXGoeScHaTDEMlx1ZmN9Mtl0CuYNIyzvzxRee2j2EUu6ivLwOArR1SDZILd540y3ONOMObAVZhJCB1vUqzTdBrz5BRxy1knwhhQRbQQHcVVp+zqxSVtRrUWgAzHzCP655ilDqXJwkV0Sy/naBr5I8Hjs0euVLj0Qq7XlcyIHtch8Lcm+rfJiDo0bYJlHfgoeRrvum0N/w+qoEVbuGpzNztGO7ISbhxeitLoP/cu7tDYxLVXuzXYCVYn2MJwjeLtlytZy74jQoKiDRLt2Zy32JSbg8hR1utaFkN8BA9OmAhbXTmdsJgleyWxlQDl3YeLaN4+L1rLrgpmmyTIBcM6oNhO9GTstjvzrwXKPHb6CmzEwFkyzBPS36bN1glcZHF5auiybAJkMMlKHMbupA7244Gw==" # test -DOTENV_VAULT_TEST="CYDEOpA8aIlJs245eFTFon8fcioqqLopkdOco1dRb5g70jfXSvgOJQ5GYuFFGOPfZkv0Q5yTHvABPiUzMP8RS0m2x+88BKpfd2TS/9ojTq/UASfOJH0/OaR2Pfj72nKqKN3PuAE6Gf0GQsjzypa25B8z0b3eRbaV/wvCxSbV+Y2F4kkw/+mm9TtYtG3oWFHyBzcAD/zGyFcXqmRDi2tIw0IHEWzD4emIS1rJDK/8Y85xILSrALAtfyPr+a2tvSNapg8kH1dv+BG5DIZBz/8xERxJQNO68altEkpsDEoue23rGEReP3gcIfF0E7/y55PEXkPUQl2RQNXIPW9DdMa0tb94ToHiOgMmOxo6oW+liIShD6ERTZIIzcA4yD+jEEFP739evtfouEHw5SeGVp++EjWN4PwB9qmow6mNgDDQyFolxzlbRvfsWQEvTGe0urXzthE+0THbHHX128XUS3NwVWLLTZEECZRvz9ZeiuAPadKoqCdydDLg5s+ufajlmw0fFJVhMQc+AP533ttlHC2vT4xVjjN59Z2yE94kap3i4HtyGqKMq7bFJWAiLT4B615xrg9zZUarlE1qM7ZqXsGpyU3kyAm+/wf4/s2hGYl8uK/ff5rmXU4X+9gobmuV77H07iuWUJESbg3dl1NQK0X4y1zA9EWcU3OB7LFrwi3ydzgi84Aq/tvOnhxn0n1PRvMwqsX4qVj3JCynUSAM3RA8ukO9RL7CcMjNnNvHi58UhLUJVCJLyZ4MS3Hcx+GdnpQ92KQvIEUFtwcLX9SaEQSRBdRJ+Knxq711E2pJrcv1uHMi7fJbaBuPsnP/ntkcWrxqmp7EqcHl29bGzOVs9js0sjlBpnW9RjvJ0AIug3ze2JzG1qoT42jSWjVVNiVTAGGPOXIFqrzx5eaTYt2bE0oWNlQCY+0bJAPqtpBJWX2sxnnuKOtYjOYXMx7uz92WJDdtmzvXZ5ymOOs0Ye7RxxBvZUSEyBws1MCP39VuOJcMidUfQGLMhiNv7EkVaPudwYQ1pD8Fvav/oQQmRNOiIo5UrbC7dZ9fd/RrS+65mDhCxHj7nni7O959reDd1y0xo+zKmD64JChJVTWN2/YyhFdqieGL8Lmtz3Z9rkigch3q4HnpVfFJmCcBGjP/KenftIW2AWU9LMHUwwEggLTU4XyO5AhhwFzzWaYWrUqxbVxhS0gsTYw/2lm+PWXFMBI7FQgax2Y9sp/eMeKGjgkykmyftqhHfmet3C7ALzdbygpkERkq9JjXQdigtmgSP1lFxxf8LkmSdWYQqHNvrTeXDJyQdL3m2m9o0zUMBnDjtd64LnslWjUBPk7uLARd3cKIK4lPGP66+jgvMJKoGJTjFcD67xko++M+/2R/ip919uTxJ+/y1mtJXFnSL/FA4jl2UsU3YaiOISO726Zv1l4AuoKfRq6x1KlfJKemh2MQVdXwXIqvqfvOIHz6/P3Eie4cwt00WDV1GbVQgR4FEAT6uQxxMaSXTGJwi4g48TprsKN+zWmmCXzAF/Zmpy9O8GNrW9of8WY6hGe9ZpMhANhTSOIH2ey3Zenkl4ZoYKZ9ywbK6db0pSFPr9SXdzzPVOeriQzQ57ij0YX7YIZJA3YROqMRRQ+xnQTnxQlxRGTJPd5abU01KVNWy9GVu/Win7wQuWg0QeDWEIS5ga3tNb4txtWzyFam9RHVPA76YjOUIgL9NMrHq4t5iE3HAr1QU4pMZ5BEeFA/nqY7kO8rNGPYrwe41cd5RcwBajPxyGH0HGxT8ovd3U2vN6fpje6NuB4e3Uz8Go2CP4U5x/b29MoR245iPjtzhn0Q/AhIMr5MVEN8Zt8DcLBtxo2E11KQMHqXuadsqT/mS65PlshVeqgITl34IIV/tf8flC143FLQNlNECqgHgE9GLEJvuTKSHLFhDC6pHstGLEyPe8gtMikN4DlyqdZ/pqV4hhNG1E+jeL3x" +DOTENV_VAULT_TEST="NjWlOUWT5HZM4whZTpS/sqrHLsWiJKV6xSQcrU2Pm4m8csvh/PJ9iQgyME+eltgHAFAdGZIrwZadFws7kCHQSQxikYq1hz513wlLhhVqk9QGuHUdM352OtsoySuHYS7SwwKog8MAsLQuJpIjmvJaCFvQ+Q15KjbHGjXgQghXELel87IEy0+lxibd1JUMfHNPzyA4EXZe3LGSVNgIfNRZWcE1vWQ+UAhDxCA1M5Y9yEQO+TB2FHyEZaFgSHC+uLgybK5SbFwyDxPY8RzLBbHtlAB5Ez2NnLWkj6wLLRNZW75wa7TserUaArVyBy8nBPqtDGpMCpMXubVHxKzFDxUHqgbhn/eVDaD7FWhVYT0PWijOpLKjtASTebEj24ea46NuLZAjMxIhb2Vr5s06PU7wmACNIi2ajdMFD+MSwKyRIvWlhcw+q2jUcZYcnqUM2F/AKp3qdSY3wLbnYr6F+dEYqXVk1A6pw+JC33eBdGVFPtskuxttg+//VfEuZOKSC7uLPHywQ8ldoljCqFLUt/+oKuhE8gzeNkX7Eb+O1scK9J4SHD2veVSafUZqBNewDjRnixGt8SMMv82XNqHbfpS7GqQKBNz5koO9wOJVvdDwSfGYY1e+RFreTUFsZtLUi6noai9+mQcwSe/cjsM77qewpfYSF+mdp4noKc3mlZTwkoVxF2zNO80uIvvkXmWraC0zqS5A+C05ppZZrjbcMN/CcKHSc1ERJaeWmbfWjjrsZlPwRtiOxLZu4Mohxl6CrZkln57lyryfhJNzjjV2/CyGhgb2VuolZp5s/hckCuSmGH7O4Uk4wiD8WaIA3j6c2YtRJ6/bWnXRVmLK+NBn50N6C0iEzixEl4nPyb8rfvI8ueDiXase28vPmXMwY8OU1uGn0L+K9M/ySSK0whxu6yLG75mv1ZLs3qQ+Z1HxUN6ZTYTNQmTAw2W2l12ZB2yZgR7Qi+uGMuI6zzLSrl7Ru+S7SIGV4PUyOc6QAtuKFx1XPNgj0ZA8Kfqx9iMnj1aHtDS1GsONuzSHOsUzW8vr1YMGEG4knEspRbIzlC7NVP4aChO/Mo4WmjZAu+oZaXxyyNXEwgxmWO4AArrsdgVCulWarr4Y1dV4639ATz81QjtjV+UieluJsAuCxqBz4p8Kw+/2k4EUBiu9FaO4pw9CDE1y2d/y9H1I+8l2NU11mPvqnNzXZgLJOLK4qV3tOFPheEK0de0WzTt4PZsneFSQbZNWmlbJDluEn53VheVfJOcUXq9KEWKwKDnzn+1dYUyWuAW6y7uXnx2ECWz1xM9DzaIwUcjv8/62NdspsSHNag8RxZ/KzHffH6ePoxpfhAogfaRjlgX+P1fpjxujY2/1LmdnH3/0kHyFuU+D/2IQC1cJMGpU7mD754JXfGS/g2d4ISVFEOTfDEv7ZwC8gRrkIMrUnk4xR4WYE3MSewZzWAQ1NcgdYrDpcSA8/wPUXnNcaP6GHZKzPoYq9SbEdOlVNkExOH+AueKGWtPm0VqprhwFOsveSsdwwAF79CKIzk1XJFCyLD+3aX7+ZvAAn/3734ggO+0tjBCy+N2pZ8+4UDABjSHA3d/UD3dNr78kzZY67xhkUu6fQSpS4nFezGgek9YpDTR8ukUbTiyovqtTmgXeb5HDBH5sZ/kaoEFgI+kskWb1u7Mk+5iZoVmPZXw2DFs7Ng2iQ+cS+KOPID534AmJ+mt8PrM92hVBlXmc96hrP4XqhIqQAImb6t9GQoDxdA8hsCUB4LQf8gKuZx4DDtrSronheYCFBLaNFNnixhFI1sVdK55VnS4/6zma6g7YRw==" diff --git a/NodeApp/src/commander/exercise/ExerciseCommand.ts b/NodeApp/src/commander/exercise/ExerciseCommand.ts index 8089092..1138f67 100644 --- a/NodeApp/src/commander/exercise/ExerciseCommand.ts +++ b/NodeApp/src/commander/exercise/ExerciseCommand.ts @@ -2,6 +2,7 @@ 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"; class ExerciseCommand extends CommanderCommand { @@ -16,6 +17,7 @@ class ExerciseCommand extends CommanderCommand { ExerciseCreateCommand.registerOnCommand(this.command); ExerciseRunCommand.registerOnCommand(this.command); ExerciseCorrectionCommand.registerOnCommand(this.command); + ExerciseListCommand.registerOnCommand(this.command); } protected async commandAction(): Promise<void> { diff --git a/NodeApp/src/commander/exercise/subcommands/ExerciseListCommand.ts b/NodeApp/src/commander/exercise/subcommands/ExerciseListCommand.ts new file mode 100644 index 0000000..14ec7d3 --- /dev/null +++ b/NodeApp/src/commander/exercise/subcommands/ExerciseListCommand.ts @@ -0,0 +1,235 @@ +// 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 searchResults = fuse.search(searchQuery).map(result => result.item); + + if (searchResults.length === 0) { + ora().info('Aucun exercice trouvé correspondant à votre recherche.'); + return; + } + + if (searchResults.length === 1) { + // Display details and members for the single matching exercise + const singleExercise = searchResults[0]; + this.displayExerciseDetails(singleExercise); + } else { + // Display only exercise names and info about viewing details + ora().info(' Plusieurs exercices trouvés correspondant à votre recherche :'); + const exerciseNames = searchResults.map(exercise => exercise.name); + console.log(' ', exerciseNames.join('\n ')); + + ora().info('Les détails sont disponibles avec la commande : `dojo exercise info <id>`.'); + } + } + + 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/managers/DojoBackendManager.ts b/NodeApp/src/managers/DojoBackendManager.ts index 8c8a5a9..c48da6e 100644 --- a/NodeApp/src/managers/DojoBackendManager.ts +++ b/NodeApp/src/managers/DojoBackendManager.ts @@ -11,6 +11,7 @@ import DojoStatusCode from '../shared/types/Dojo/DojoStatusCode.js'; import * as Gitlab from '@gitbeaker/rest'; import DojoBackendHelper from '../sharedByClients/helpers/Dojo/DojoBackendHelper.js'; import GitlabPipelineStatus from '../shared/types/Gitlab/GitlabPipelineStatus.js'; +import Result from "../sharedByClients/models/Result"; class DojoBackendManager { @@ -249,6 +250,71 @@ class DojoBackendManager { return false; } } + + public async getUserExercises(): Promise<Exercise[] | undefined> { + try { + const response = await axios.get<DojoBackendResponse<Exercise[]>>(DojoBackendHelper.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>(DojoBackendHelper.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>>(DojoBackendHelper.getApiUrl(ApiRoute.EXERCISE_DELETE).replace('{{exerciseId}}', exerciseId))).data.data; + } + + + + + + + + + + + public async getExerciseMembers(exerciseId: string): Promise<Array<User>> { + return (await axios.get<DojoBackendResponse<Array<User>>>(DojoBackendHelper.getApiUrl(ApiRoute.EXERCISE_MEMBER).replace('{{exerciseId}}', exerciseId))).data.data; + } + + + + public async getExerciseResults(exerciseId: string): Promise<Result[]> { + try { + const response = await axios.get(DojoBackendHelper.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[]>>(DojoBackendHelper.getApiUrl(ApiRoute.TEACHERS)); + const users = response.data.data; + return users + } catch (error) { + console.error('Error fetching professors:', error); + return undefined; + } + } } -- GitLab