Commit ae23b9b1 authored by janithgamage1.ed's avatar janithgamage1.ed

Merge branch 'development' into IT20251000

parents 4562a5f5 a3bf1116
{
"cSpell.words": [
"formik",
"Janith",
"leaderboard",
"SLIIT"
......
models/*
!models/
\ No newline at end of file
!models/
*.h5
\ No newline at end of file
{
"cells": [
{
"cell_type": "code",
"execution_count": 4,
"id": "4e8edf74",
"metadata": {},
"outputs": [],
"source": [
"import cv2\n",
"import numpy as np"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "e11e6436",
"metadata": {},
"outputs": [],
"source": [
"image = cv2.imread('D:/RP/project/2023-029/Project/Backend\\ML_Models/sign_language_to_text/scene00001.png')\n",
"\n",
"def extract_hand_shape(image):\n",
" gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)\n",
" blurred = cv2.GaussianBlur(gray, (5, 5), 0)\n",
" _, thresholded = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)\n",
" contours, _ = cv2.findContours(thresholded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
" contours = sorted(contours, key=cv2.contourArea, reverse=True)\n",
" hand_contour = contours[0]\n",
" hand_shape = np.zeros_like(image)\n",
" cv2.drawContours(hand_shape, [hand_contour], 0, (255, 255, 255), thickness=cv2.FILLED)\n",
" return hand_shape\n",
"\n",
"hand_shape = extract_hand_shape(image)\n",
"\n",
"# Display the hand shape\n",
"cv2.imshow('Hand Shape', hand_shape)\n",
"cv2.waitKey(0)\n",
"cv2.destroyAllWindows()"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "157e18d5",
"metadata": {},
"outputs": [],
"source": [
"# Convert the image to grayscale\n",
"gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n",
"\n",
"# Apply Gaussian blur to reduce noise\n",
"blurred = cv2.GaussianBlur(gray, (5, 5), 0)\n",
"\n",
"# Apply adaptive thresholding to segment the hand from the background\n",
"_, thresholded = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)\n",
"\n",
"# Find contours of the hand\n",
"contours, _ = cv2.findContours(thresholded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
"\n",
"# Sort contours by area in descending order\n",
"contours = sorted(contours, key=cv2.contourArea, reverse=True)\n",
"\n",
"# Extract the largest contour (hand region)\n",
"hand_contour = contours[0]\n",
"\n",
"# Create a black image of the same size as the input image\n",
"hand_shape = np.zeros_like(image)\n",
"\n",
"# Draw the hand contour on the black image\n",
"cv2.drawContours(hand_shape, [hand_contour], 0, (255, 255, 255), thickness=cv2.FILLED)\n",
"\n",
"# Display the hand shape\n",
"cv2.imshow('Hand Shape', hand_shape)\n",
"cv2.waitKey(0)\n",
"cv2.destroyAllWindows()"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "93d1ae9d",
"metadata": {},
"outputs": [],
"source": [
"import cv2\n",
"import numpy as np\n",
"\n",
"\n",
"IMG_SIZE = 224\n",
"\n",
"frame = cv2.imread('D:/RP/project/2023-029/Project/Backend/ML_Models/sign_language_to_text/test_image.png')\n",
"\n",
"def extract_hand_shape(image):\n",
" gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)\n",
" blurred = cv2.GaussianBlur(gray, (5, 5), 0)\n",
" _, thresholded = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)\n",
" contours, _ = cv2.findContours(thresholded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
" contours = sorted(contours, key=cv2.contourArea, reverse=True)\n",
" hand_contour = contours[0]\n",
" hand_shape = np.zeros_like(image)\n",
" cv2.drawContours(hand_shape, [hand_contour], 0, (255, 255, 255), thickness=cv2.FILLED)\n",
" return hand_shape\n",
"\n",
"frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)\n",
"frame = cv2.resize(frame, (IMG_SIZE, IMG_SIZE))\n",
"# frame = extract_hand_shape(frame)\n",
"# frame = np.array([frame], dtype=np.float32) / 255.0\n",
"\n",
"\n",
"# Display the hand shape\n",
"cv2.imshow('Hand Shape', frame)\n",
"cv2.waitKey(0)\n",
"cv2.destroyAllWindows()"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "e7e05125",
"metadata": {},
"outputs": [
{
"ename": "error",
"evalue": "OpenCV(4.7.0) D:/a/opencv-python/opencv-python/opencv/modules/highgui/src/precomp.hpp:155: error: (-215:Assertion failed) src_depth != CV_16F && src_depth != CV_32S in function 'convertToShow'\n",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31merror\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m~\\AppData\\Local\\Temp\\ipykernel_19904\\1480809324.py\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m 31\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 32\u001b[0m \u001b[1;31m# Display the hand shape with white background\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 33\u001b[1;33m \u001b[0mcv2\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mimshow\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'Hand Shape with White Background'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mhand_shape_with_white_bg\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 34\u001b[0m \u001b[0mcv2\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mwaitKey\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 35\u001b[0m \u001b[0mcv2\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mdestroyAllWindows\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
"\u001b[1;31merror\u001b[0m: OpenCV(4.7.0) D:/a/opencv-python/opencv-python/opencv/modules/highgui/src/precomp.hpp:155: error: (-215:Assertion failed) src_depth != CV_16F && src_depth != CV_32S in function 'convertToShow'\n"
]
}
],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
{
"cells": [
{
"cell_type": "code",
"execution_count": 17,
"id": "4e8edf74",
"metadata": {},
"outputs": [],
"source": [
"import cv2\n",
"import numpy as np"
]
},
{
"cell_type": "code",
"execution_count": 55,
"id": "e11e6436",
"metadata": {},
"outputs": [],
"source": [
"# works well with white background buth have some issues eith other backgrounds \n",
"\n",
"# image = cv2.imread('C:/Users/HP Pavilion/Downloads/images (1).jpg')\n",
"image = cv2.imread('D:/RP/project/2023-029/Project/Backend\\ML_Models/sign_language_to_text/scene00001.png')\n",
"\n",
"def extract_hand_shape(image):\n",
" gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)\n",
" blurred = cv2.GaussianBlur(gray, (5, 5), 0)\n",
" _, thresholded = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)\n",
" contours, _ = cv2.findContours(thresholded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
" contours = sorted(contours, key=cv2.contourArea, reverse=True)\n",
" hand_contour = contours[0]\n",
" hand_shape = np.zeros_like(image)\n",
" cv2.drawContours(hand_shape, [hand_contour], 0, (255, 255, 255), thickness=cv2.FILLED)\n",
" return hand_shape\n",
"\n",
"hand_shape = extract_hand_shape(image)\n",
"\n",
"# Display the hand shape\n",
"cv2.imshow('Hand Shape', hand_shape)\n",
"cv2.waitKey(0)\n",
"cv2.destroyAllWindows()"
]
},
{
"cell_type": "code",
"execution_count": 57,
"id": "157e18d5",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 19,
"id": "e7e05125",
"metadata": {},
"outputs": [],
"source": [
"# This works for any background, but does not hapture the fist area\n",
"import cv2\n",
"import numpy as np\n",
"# image = cv2.imread('D:/RP/project/2023-029/Project/Backend\\ML_Models/sign_language_to_text/scene00001.png')\n",
"image = cv2.imread('C:/Users/HP Pavilion/Downloads/images (1).jpg')\n",
"\n",
"def extract_hand_shape(image):\n",
" gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)\n",
" blurred = cv2.GaussianBlur(gray, (5, 5), 0)\n",
" thresholded = cv2.adaptiveThreshold(blurred, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2)\n",
" contours, _ = cv2.findContours(thresholded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
" contours = sorted(contours, key=cv2.contourArea, reverse=True)\n",
" hand_contour = contours[0]\n",
" hand_shape = np.zeros_like(image)\n",
" cv2.drawContours(hand_shape, [hand_contour], 0, (255, 255, 255), thickness=cv2.FILLED)\n",
" return hand_shape\n",
"\n",
"hand_shape = extract_hand_shape(image)\n",
"# Display the hand shape\n",
"cv2.imshow('Hand Shape', hand_shape)\n",
"cv2.waitKey(0)\n",
"cv2.destroyAllWindows()"
]
},
{
"cell_type": "markdown",
"id": "ae0a7cb5",
"metadata": {},
"source": [
"### Capture Hand"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "1c04daef",
"metadata": {},
"outputs": [
{
"ename": "error",
"evalue": "OpenCV(4.7.0) D:\\a\\opencv-python\\opencv-python\\opencv\\modules\\highgui\\src\\window.cpp:971: error: (-215:Assertion failed) size.width>0 && size.height>0 in function 'cv::imshow'\n",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31merror\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m~\\AppData\\Local\\Temp\\ipykernel_3024\\2889311055.py\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m 29\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 30\u001b[0m \u001b[1;31m# Display the result\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 31\u001b[1;33m \u001b[0mcv2\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mimshow\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"Area Above Elbow\"\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0marea_above_elbow\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 32\u001b[0m \u001b[0mcv2\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mwaitKey\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 33\u001b[0m \u001b[0mcv2\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mdestroyAllWindows\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
"\u001b[1;31merror\u001b[0m: OpenCV(4.7.0) D:\\a\\opencv-python\\opencv-python\\opencv\\modules\\highgui\\src\\window.cpp:971: error: (-215:Assertion failed) size.width>0 && size.height>0 in function 'cv::imshow'\n"
]
}
],
"source": [
"import cv2\n",
"import numpy as np\n",
"import mediapipe as mp\n",
"# Load the image\n",
"image = cv2.imread('C:/Users/HP Pavilion/Downloads/test_sign.jpeg')\n",
"\n",
"image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n",
"\n",
"# Initialize Mediapipe solutions\n",
"mp_holistic = mp.solutions.holistic\n",
"\n",
"# Initialize the holistic model\n",
"holistic = mp_holistic.Holistic()\n",
"\n",
"# Process the image with Mediapipe\n",
"results = holistic.process(image_rgb)\n",
"\n",
"# Extract the left elbow and hand landmarks\n",
"left_elbow_landmark = results.left_hand_landmarks.landmark[mp_holistic.HandLandmark.WRIST]\n",
"left_hand_landmark = results.left_hand_landmarks.landmark[mp_holistic.HandLandmark.PINKY_MCP]\n",
"\n",
"# Convert the landmark coordinates to image pixels\n",
"image_height, image_width, _ = image.shape\n",
"left_elbow_px = int(left_elbow_landmark.x * image_width), int(left_elbow_landmark.y * image_height)\n",
"left_hand_px = int(left_hand_landmark.x * image_width), int(left_hand_landmark.y * image_height)\n",
"\n",
"# Calculate the area above the elbow\n",
"area_above_elbow = image[0:left_elbow_px[1], left_elbow_px[0]:left_hand_px[0]]\n",
"\n",
"# Display the result\n",
"cv2.imshow(\"Area Above Elbow\", area_above_elbow)\n",
"cv2.waitKey(0)\n",
"cv2.destroyAllWindows()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3ead61b7",
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"# This works for any background, but does not hapture the fist area\n",
"import cv2\n",
"import numpy as np\n",
"\n",
"IMG_SIZE = 224 # image size\n",
"\n",
"# image = cv2.imread('C:/Users/HP Pavilion/Downloads/images (1).jpg')\n",
"\n",
"\n",
"image = cv2.imread('D:/RP/project/2023-029/Project/Backend/ML_Models/sign_language_to_text/test_image.png')\n",
"\n",
"def extract_hand_shape(image):\n",
" gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)\n",
" blurred = cv2.GaussianBlur(gray, (5, 5), 0)\n",
" _, thresholded = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)\n",
" contours, _ = cv2.findContours(thresholded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
" contours = sorted(contours, key=cv2.contourArea, reverse=True)\n",
" hand_contour = contours[0]\n",
" hand_shape = np.zeros_like(image)\n",
" cv2.drawContours(hand_shape, [hand_contour], 0, (255, 255, 255), thickness=cv2.FILLED)\n",
" return hand_shape\n",
"\n",
"\n",
"frame = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n",
"frame = cv2.resize(frame, (IMG_SIZE, IMG_SIZE))\n",
"frame = extract_hand_shape(frame)\n",
"frame = np.array([frame], dtype=np.float32) / 255.0\n",
"\n",
"\n",
"\n",
"\n",
"# Display the hand shape\n",
"cv2.imshow('Hand Shape', hand_shape)\n",
"cv2.waitKey(0)\n",
"cv2.destroyAllWindows()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
{
"cells": [
{
"cell_type": "code",
"execution_count": 4,
"id": "4e8edf74",
"metadata": {},
"outputs": [],
"source": [
"import cv2\n",
"import numpy as np"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "e11e6436",
"metadata": {},
"outputs": [],
"source": [
"image = cv2.imread('D:/RP/project/2023-029/Project/Backend\\ML_Models/sign_language_to_text/scene00001.png')\n",
"\n",
"def extract_hand_shape(image):\n",
" gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)\n",
" blurred = cv2.GaussianBlur(gray, (5, 5), 0)\n",
" _, thresholded = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)\n",
" contours, _ = cv2.findContours(thresholded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
" contours = sorted(contours, key=cv2.contourArea, reverse=True)\n",
" hand_contour = contours[0]\n",
" hand_shape = np.zeros_like(image)\n",
" cv2.drawContours(hand_shape, [hand_contour], 0, (255, 255, 255), thickness=cv2.FILLED)\n",
" return hand_shape\n",
"\n",
"hand_shape = extract_hand_shape(image)\n",
"\n",
"# Display the hand shape\n",
"cv2.imshow('Hand Shape', hand_shape)\n",
"cv2.waitKey(0)\n",
"cv2.destroyAllWindows()"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "157e18d5",
"metadata": {},
"outputs": [],
"source": [
"# Convert the image to grayscale\n",
"gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n",
"\n",
"# Apply Gaussian blur to reduce noise\n",
"blurred = cv2.GaussianBlur(gray, (5, 5), 0)\n",
"\n",
"# Apply adaptive thresholding to segment the hand from the background\n",
"_, thresholded = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)\n",
"\n",
"# Find contours of the hand\n",
"contours, _ = cv2.findContours(thresholded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
"\n",
"# Sort contours by area in descending order\n",
"contours = sorted(contours, key=cv2.contourArea, reverse=True)\n",
"\n",
"# Extract the largest contour (hand region)\n",
"hand_contour = contours[0]\n",
"\n",
"# Create a black image of the same size as the input image\n",
"hand_shape = np.zeros_like(image)\n",
"\n",
"# Draw the hand contour on the black image\n",
"cv2.drawContours(hand_shape, [hand_contour], 0, (255, 255, 255), thickness=cv2.FILLED)\n",
"\n",
"# Display the hand shape\n",
"cv2.imshow('Hand Shape', hand_shape)\n",
"cv2.waitKey(0)\n",
"cv2.destroyAllWindows()"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "93d1ae9d",
"metadata": {},
"outputs": [
{
"ename": "error",
"evalue": "OpenCV(4.7.0) D:\\a\\opencv-python\\opencv-python\\opencv\\modules\\highgui\\src\\window_w32.cpp:124: error: (-215:Assertion failed) bmi && width >= 0 && height >= 0 && (bpp == 8 || bpp == 24 || bpp == 32) in function 'FillBitmapInfo'\n",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31merror\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m~\\AppData\\Local\\Temp\\ipykernel_16572\\1170496581.py\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m 25\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 26\u001b[0m \u001b[1;31m# Display the hand shape\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 27\u001b[1;33m \u001b[0mcv2\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mimshow\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'Hand Shape'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mframe\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 28\u001b[0m \u001b[0mcv2\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mwaitKey\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 29\u001b[0m \u001b[0mcv2\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mdestroyAllWindows\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
"\u001b[1;31merror\u001b[0m: OpenCV(4.7.0) D:\\a\\opencv-python\\opencv-python\\opencv\\modules\\highgui\\src\\window_w32.cpp:124: error: (-215:Assertion failed) bmi && width >= 0 && height >= 0 && (bpp == 8 || bpp == 24 || bpp == 32) in function 'FillBitmapInfo'\n"
]
}
],
"source": [
"import cv2\n",
"import numpy as np\n",
"\n",
"\n",
"IMG_SIZE = 224\n",
"\n",
"frame = cv2.imread('D:/RP/project/2023-029/Project/Backend/ML_Models/sign_language_to_text/test_image.png')\n",
"\n",
"def extract_hand_shape(image):\n",
" gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)\n",
" blurred = cv2.GaussianBlur(gray, (5, 5), 0)\n",
" _, thresholded = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)\n",
" contours, _ = cv2.findContours(thresholded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
" contours = sorted(contours, key=cv2.contourArea, reverse=True)\n",
" hand_contour = contours[0]\n",
" hand_shape = np.zeros_like(image)\n",
" cv2.drawContours(hand_shape, [hand_contour], 0, (255, 255, 255), thickness=cv2.FILLED)\n",
" return hand_shape\n",
"\n",
"frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)\n",
"frame = cv2.resize(frame, (IMG_SIZE, IMG_SIZE))\n",
"frame = extract_hand_shape(frame)\n",
"frame = np.array([frame], dtype=np.float32) / 255.0\n",
"\n",
"\n",
"# Display the hand shape\n",
"cv2.imshow('Hand Shape', frame)\n",
"cv2.waitKey(0)\n",
"cv2.destroyAllWindows()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
{
"cells": [
{
"cell_type": "code",
"execution_count": 17,
"id": "4e8edf74",
"metadata": {},
"outputs": [],
"source": [
"import cv2\n",
"import numpy as np"
]
},
{
"cell_type": "code",
"execution_count": 55,
"id": "e11e6436",
"metadata": {},
"outputs": [],
"source": [
"# works well with white background buth have some issues eith other backgrounds \n",
"\n",
"# image = cv2.imread('C:/Users/HP Pavilion/Downloads/images (1).jpg')\n",
"image = cv2.imread('D:/RP/project/2023-029/Project/Backend\\ML_Models/sign_language_to_text/scene00001.png')\n",
"\n",
"def extract_hand_shape(image):\n",
" gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)\n",
" blurred = cv2.GaussianBlur(gray, (5, 5), 0)\n",
" _, thresholded = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)\n",
" contours, _ = cv2.findContours(thresholded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
" contours = sorted(contours, key=cv2.contourArea, reverse=True)\n",
" hand_contour = contours[0]\n",
" hand_shape = np.zeros_like(image)\n",
" cv2.drawContours(hand_shape, [hand_contour], 0, (255, 255, 255), thickness=cv2.FILLED)\n",
" return hand_shape\n",
"\n",
"hand_shape = extract_hand_shape(image)\n",
"\n",
"# Display the hand shape\n",
"cv2.imshow('Hand Shape', hand_shape)\n",
"cv2.waitKey(0)\n",
"cv2.destroyAllWindows()"
]
},
{
"cell_type": "code",
"execution_count": 57,
"id": "157e18d5",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 19,
"id": "e7e05125",
"metadata": {},
"outputs": [],
"source": [
"# This works for any background, but does not hapture the fist area\n",
"import cv2\n",
"import numpy as np\n",
"# image = cv2.imread('D:/RP/project/2023-029/Project/Backend\\ML_Models/sign_language_to_text/scene00001.png')\n",
"image = cv2.imread('C:/Users/HP Pavilion/Downloads/images (1).jpg')\n",
"\n",
"def extract_hand_shape(image):\n",
" gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)\n",
" blurred = cv2.GaussianBlur(gray, (5, 5), 0)\n",
" thresholded = cv2.adaptiveThreshold(blurred, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2)\n",
" contours, _ = cv2.findContours(thresholded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
" contours = sorted(contours, key=cv2.contourArea, reverse=True)\n",
" hand_contour = contours[0]\n",
" hand_shape = np.zeros_like(image)\n",
" cv2.drawContours(hand_shape, [hand_contour], 0, (255, 255, 255), thickness=cv2.FILLED)\n",
" return hand_shape\n",
"\n",
"hand_shape = extract_hand_shape(image)\n",
"# Display the hand shape\n",
"cv2.imshow('Hand Shape', hand_shape)\n",
"cv2.waitKey(0)\n",
"cv2.destroyAllWindows()"
]
},
{
"cell_type": "markdown",
"id": "ae0a7cb5",
"metadata": {},
"source": [
"### Capture Hand"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "1c04daef",
"metadata": {},
"outputs": [
{
"ename": "error",
"evalue": "OpenCV(4.7.0) D:\\a\\opencv-python\\opencv-python\\opencv\\modules\\highgui\\src\\window.cpp:971: error: (-215:Assertion failed) size.width>0 && size.height>0 in function 'cv::imshow'\n",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31merror\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m~\\AppData\\Local\\Temp\\ipykernel_3024\\2889311055.py\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m 29\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 30\u001b[0m \u001b[1;31m# Display the result\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 31\u001b[1;33m \u001b[0mcv2\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mimshow\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"Area Above Elbow\"\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0marea_above_elbow\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 32\u001b[0m \u001b[0mcv2\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mwaitKey\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 33\u001b[0m \u001b[0mcv2\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mdestroyAllWindows\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
"\u001b[1;31merror\u001b[0m: OpenCV(4.7.0) D:\\a\\opencv-python\\opencv-python\\opencv\\modules\\highgui\\src\\window.cpp:971: error: (-215:Assertion failed) size.width>0 && size.height>0 in function 'cv::imshow'\n"
]
}
],
"source": [
"import cv2\n",
"import numpy as np\n",
"import mediapipe as mp\n",
"# Load the image\n",
"image = cv2.imread('C:/Users/HP Pavilion/Downloads/test_sign.jpeg')\n",
"\n",
"image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n",
"\n",
"# Initialize Mediapipe solutions\n",
"mp_holistic = mp.solutions.holistic\n",
"\n",
"# Initialize the holistic model\n",
"holistic = mp_holistic.Holistic()\n",
"\n",
"# Process the image with Mediapipe\n",
"results = holistic.process(image_rgb)\n",
"\n",
"# Extract the left elbow and hand landmarks\n",
"left_elbow_landmark = results.left_hand_landmarks.landmark[mp_holistic.HandLandmark.WRIST]\n",
"left_hand_landmark = results.left_hand_landmarks.landmark[mp_holistic.HandLandmark.PINKY_MCP]\n",
"\n",
"# Convert the landmark coordinates to image pixels\n",
"image_height, image_width, _ = image.shape\n",
"left_elbow_px = int(left_elbow_landmark.x * image_width), int(left_elbow_landmark.y * image_height)\n",
"left_hand_px = int(left_hand_landmark.x * image_width), int(left_hand_landmark.y * image_height)\n",
"\n",
"# Calculate the area above the elbow\n",
"area_above_elbow = image[0:left_elbow_px[1], left_elbow_px[0]:left_hand_px[0]]\n",
"\n",
"# Display the result\n",
"cv2.imshow(\"Area Above Elbow\", area_above_elbow)\n",
"cv2.waitKey(0)\n",
"cv2.destroyAllWindows()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3ead61b7",
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"# This works for any background, but does not hapture the fist area\n",
"import cv2\n",
"import numpy as np\n",
"\n",
"IMG_SIZE = 224 # image size\n",
"\n",
"# image = cv2.imread('C:/Users/HP Pavilion/Downloads/images (1).jpg')\n",
"\n",
"\n",
"image = cv2.imread('D:/RP/project/2023-029/Project/Backend/ML_Models/sign_language_to_text/test_image.png')\n",
"\n",
"def extract_hand_shape(image):\n",
" gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)\n",
" blurred = cv2.GaussianBlur(gray, (5, 5), 0)\n",
" _, thresholded = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)\n",
" contours, _ = cv2.findContours(thresholded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
" contours = sorted(contours, key=cv2.contourArea, reverse=True)\n",
" hand_contour = contours[0]\n",
" hand_shape = np.zeros_like(image)\n",
" cv2.drawContours(hand_shape, [hand_contour], 0, (255, 255, 255), thickness=cv2.FILLED)\n",
" return hand_shape\n",
"\n",
"\n",
"frame = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n",
"frame = cv2.resize(frame, (IMG_SIZE, IMG_SIZE))\n",
"frame = extract_hand_shape(frame)\n",
"frame = np.array([frame], dtype=np.float32) / 255.0\n",
"\n",
"\n",
"\n",
"\n",
"# Display the hand shape\n",
"cv2.imshow('Hand Shape', hand_shape)\n",
"cv2.waitKey(0)\n",
"cv2.destroyAllWindows()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
This source diff could not be displayed because it is too large. You can view the blob instead.
import Curriculum from '../models/curriculum.model.js';
import Tutorial from '../models/tutorial.model.js';
export const getAllCurriculums = async (req, res) => {
try {
const curriculums = await Curriculum.find();
const curriculums = await Curriculum.find().populate("tutorials");
res.status(200).json(curriculums);
} catch (error) {
res.status(500).json({ message: error.message });
}
}
};
export const getCurriculumById = async (req, res) => {
const { id } = req.params;
try {
const curriculum = await Curriculum.findById(id);
const curriculum = await Curriculum.findById(id).populate("tutorials");
if (!curriculum) {
return res.status(404).json({ message: 'Curriculum not found' });
}
res.status(200).json(curriculum);
} catch (error) {
res.status(404).json({ message: 'Curriculum not found' });
res.status(500).json({ message: error.message });
}
}
};
export const createCurriculum = async (req, res) => {
const curriculumData = req.body;
try {
const newCurriculum = new Curriculum(curriculumData);
// Calculate total tutorial mark
let totalTutorialMark = 0;
for (const tutorialId of curriculumData.tutorials) {
const tutorial = await Tutorial.findById(tutorialId);
totalTutorialMark += tutorial.tutorialMarks;
}
newCurriculum.curriculumMark = totalTutorialMark;
await newCurriculum.save();
res.status(201).json(newCurriculum);
} catch (error) {
res.status(400).json({ message: error.message });
}
}
export const updateCurriculum = async (req, res) => {
const { id } = req.params;
const updatedCurriculum = req.body;
try {
const curriculum = await Curriculum.findById(id);
// Calculate total tutorial mark
let totalTutorialMark = 0;
for (const tutorialId of updatedCurriculum.tutorials) {
const tutorial = await Tutorial.findById(tutorialId);
totalTutorialMark += tutorial.tutorialMarks;
}
updatedCurriculum.curriculumMark = totalTutorialMark;
const result = await Curriculum.findByIdAndUpdate(id, updatedCurriculum, { new: true });
res.status(200).json(result);
} catch (error) {
......
import { exec } from "child_process";
import multer from "multer"
export const marksCalculator = async (req, res) => {
const imageData = req.file.buffer.toString('base64');
const targetClass = req.body.class;
const { curriculumIndex, tutorialIndex } = req.params;
const status = "";
// console.log(curriculumIndex, tutorialIndex);
try {
if (curriculumIndex == 1 && tutorialIndex == 1) {
// Run Python script to perform prediction
const pythonProcess = exec('python prediction_config/C1T1/predict.py', (error, stdout, stderr) => {
if (error) {
console.error(error);
return res.status(500).json({ error: 'An error occurred' });
}
const [predicted_class_name, confidence] = stdout.trim().split(',');
const parsedConfidence = parseFloat(confidence).toFixed(2);
let status = "";
if (predicted_class_name === targetClass && parsedConfidence > 85) {
status = "pass";
} else {
status = "fail";
}
res.status(200).json({
code: "01",
result: {
predicted_class_name,
confidence: parsedConfidence,
status
}
});
});
pythonProcess.stdin.write(`${imageData}\n${targetClass}`);
pythonProcess.stdin.end();
} else {
return res.status(400).json({ code: "02", message: "Curriculum Index or Tutorial Index Invalid" })
}
} catch (error) {
res.status(500).json({ code: "00", message: "Something went wrong" })
}
}
\ No newline at end of file
......@@ -21,6 +21,11 @@ export const getTutorialById = async (req, res) => {
export const createTutorial = async (req, res) => {
const tutorialData = req.body;
// Calculate total tutorial marks based on task item marks
const totalTaskMarks = tutorialData.taskItems.reduce((total, task) => total + (task.taskItemMark || 0), 0);
tutorialData.tutorialMarks = totalTaskMarks;
try {
const newTutorial = new Tutorial(tutorialData);
await newTutorial.save();
......@@ -32,9 +37,14 @@ export const createTutorial = async (req, res) => {
export const updateTutorial = async (req, res) => {
const { id } = req.params;
const updatedTutorial = req.body;
const updatedTutorialData = req.body;
// Calculate total tutorial marks based on updated task item marks
const totalTaskMarks = updatedTutorialData.taskItems.reduce((total, task) => total + (task.taskItemMark || 0), 0);
updatedTutorialData.tutorialMarks = totalTaskMarks;
try {
const result = await Tutorial.findByIdAndUpdate(id, updatedTutorial, { new: true });
const result = await Tutorial.findByIdAndUpdate(id, updatedTutorialData, { new: true });
res.status(200).json(result);
} catch (error) {
res.status(404).json({ message: 'Tutorial not found' });
......
import UserProgress from '../models/userProgress.model.js';
import UserProgress from "../models/userProgress.model.js";
// Logic to subscribe to a curriculum for a user
export const subscribeCurriculum = async (req, res) => {
const { userId, curriculum } = req.body;
try {
let userProgress = await UserProgress.findOne({ userId });
if (!userProgress) {
// If there is no user progress record for this user, create a new one
userProgress = new UserProgress({
userId,
curriculums: [curriculum], // Add the curriculum to the curriculums array
});
} else {
// If there is an existing user progress record, check if the curriculum already exists
const existingCurriculumIndex = userProgress.curriculums.findIndex(c => c.curriculumCode === curriculum.curriculumCode);
if (existingCurriculumIndex !== -1) {
// If the curriculum exists, update it
userProgress.curriculums[existingCurriculumIndex] = curriculum;
} else {
// If the curriculum doesn't exist, add it to the array
userProgress.curriculums.push(curriculum);
}
}
// Update the totalCurriculumsMarks based on curriculum marks
const totalCurriculumsMarks = userProgress.curriculums.reduce((total, curriculum) => total + curriculum.curriculumMark, 0);
userProgress.totalCurriculumsMarks = totalCurriculumsMarks;
// Save the user progress record
await userProgress.save();
res.status(200).json({ message: 'Curriculum subscription successful' });
} catch (error) {
console.error(error);
res.status(500).json({ error: 'Internal server error' });
}
};
// Get user's curriculum subscriptions and progress
export const getUserProgress = async (req, res) => {
const userId = req.params.userId;
try {
const userProgress = await UserProgress.findOne({ userId }).populate('curriculumId tutorialProgress.tutorialId');
res.status(200).json(userProgress);
const userProgress = await UserProgress.findOne({ userId });
if (!userProgress) {
return res.status(404).json({ message: 'User progress not found' });
}
res.status(200).json({ userProgress });
} catch (error) {
res.status(500).json({ message: error.message });
console.error(error);
res.status(500).json({ error: 'Internal server error' });
}
}
};
export const updateUserProgress = async (req, res) => {
const userId = req.params.userId;
const { curriculumId, tutorialId, completed, marks } = req.body;
// Update task item progress and calculate overall progress
export const updateTaskItemProgress = async (req, res) => {
const { userId, curriculumCode, tutorialCode, taskItemTitle, taskItemMarkUser, taskItemSpentTime } = req.body;
try {
let userProgress = await UserProgress.findOne({ userId });
const userProgress = await UserProgress.findOne({ userId });
if (!userProgress) {
userProgress = new UserProgress({ userId });
return res.status(404).json({ message: 'User progress not found' });
}
const curriculumProgress = userProgress.curriculumId.find(prog => prog.curriculumId.equals(curriculumId));
// Find the curriculum, tutorial, and task item to update
const curriculumIndex = userProgress.curriculums.findIndex(c => c.curriculumCode === curriculumCode);
if (curriculumIndex === -1) {
return res.status(404).json({ message: 'Curriculum not found' });
}
if (!curriculumProgress) {
userProgress.curriculumId.push({ curriculumId });
const tutorialIndex = userProgress.curriculums[curriculumIndex].tutorials.findIndex(t => t.tutorialCode === tutorialCode);
if (tutorialIndex === -1) {
return res.status(404).json({ message: 'Tutorial not found' });
}
const tutorialProgress = curriculumProgress.tutorialProgress.find(prog => prog.tutorialId.equals(tutorialId));
if (!tutorialProgress) {
curriculumProgress.tutorialProgress.push({ tutorialId, completed });
} else {
tutorialProgress.completed = completed;
const taskItemIndex = userProgress.curriculums[curriculumIndex].tutorials[tutorialIndex].taskItems.findIndex(item => item.title === taskItemTitle);
if (taskItemIndex === -1) {
return res.status(404).json({ message: 'Task item not found' });
}
userProgress.marks = marks;
// Update task item progress
userProgress.curriculums[curriculumIndex].tutorials[tutorialIndex].taskItems[taskItemIndex].taskItemMarkUser = taskItemMarkUser;
userProgress.curriculums[curriculumIndex].tutorials[tutorialIndex].taskItems[taskItemIndex].taskItemSpentTime = taskItemSpentTime;
// Calculate total task marks and spent time for the tutorial
const tutorial = userProgress.curriculums[curriculumIndex].tutorials[tutorialIndex];
tutorial.tutorialMarkUser = tutorial.taskItems.reduce((total, item) => total + item.taskItemMarkUser, 0);
tutorial.tutorialSpentTime = tutorial.taskItems.reduce((total, item) => total + item.taskItemSpentTime, 0);
// Calculate total tutorial marks and spent time for the curriculum
const curriculum = userProgress.curriculums[curriculumIndex];
curriculum.curriculumMarkUser = curriculum.tutorials.reduce((total, t) => total + t.tutorialMarkUser, 0);
curriculum.curriculumSpentTime = curriculum.tutorials.reduce((total, t) => total + t.tutorialSpentTime, 0);
// Calculate total curriculum marks and spent time for the user
userProgress.totalCurriculumsMarks = userProgress.curriculums.reduce((total, c) => total + c.curriculumMarkUser, 0);
userProgress.totalCurriculumSpentTime = userProgress.curriculums.reduce((total, c) => total + c.curriculumSpentTime, 0);
// Save the updated user progress
await userProgress.save();
res.status(200).json(userProgress);
res.status(200).json({ message: 'Task item progress updated successfully' });
} catch (error) {
res.status(500).json({ message: error.message });
console.error(error);
res.status(500).json({ error: 'Internal server error' });
}
}
};
......@@ -84,24 +84,18 @@ const curriculums = [
},
{
"tutorialCode": "02",
"tutorialTitle": "Learn the Basics of Sign Language",
"tutorialTitle": "Everyday Vocabulary in Sign Language",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1YACBlu7X-O7-DKv5DoW3AM9kgfT7Yhdc",
"tutorialContent": "Introduce the concept of sign language and its importance.\nTeach basic greetings and expressions, such as hello, goodbye, thank you, and sorry.\nProvide visual demonstrations and practice exercises for learners to practice these basic signs."
},
"tutorialContent": "Teach signs for everyday objects and activities, such as eat, drink, sleep, book, pen, etc.\nIntroduce signs for common words used in daily life.\nProvide visual demonstrations and interactive exercises for learners to practice using these signs."
},
{
"tutorialCode": "03",
"tutorialTitle": "Family Signs in Sign Language",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1YACBlu7X-O7-DKv5DoW3AM9kgfT7Yhdc",
"tutorialContent": "Teach signs for family members, such as mother, father, sister, brother, etc.\nIntroduce signs for common family-related words, such as family, love, and home.\nProvide visual demonstrations and practice exercises for learners to practice these family signs."
},
},
{
"tutorialCode": "04",
"tutorialTitle": "Everyday Vocabulary in Sign Language",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1YACBlu7X-O7-DKv5DoW3AM9kgfT7Yhdc",
"tutorialContent": "Teach signs for everyday objects and activities, such as eat, drink, sleep, book, pen, etc.\nIntroduce signs for common words used in daily life.\nProvide visual demonstrations and interactive exercises for learners to practice using these signs."
},
{
"tutorialCode": "05",
"tutorialTitle": "Basic Conversational Phrases in Sign Language",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1YACBlu7X-O7-DKv5DoW3AM9kgfT7Yhdc",
"tutorialContent": "Teach simple conversational phrases, such as \"What is your name?\" or \"How are you?\"\nIntroduce signs for common question words and phrases.\nProvide visual demonstrations and practice exercises for learners to practice these conversational phrases."
......
import mongoose from "mongoose";
const tutorialSchema = new mongoose.Schema({
tutorialCode: String,
tutorialTitle: String,
tutorialImage: String,
// Additional fields for tutorial content
});
const curriculumSchema = new mongoose.Schema({
curriculumCode: String,
curriculumLevel: String,
curriculumName: String,
curriculumImage: String,
tutorials: [tutorialSchema], // Embed tutorials as subdocuments
// Additional fields for curriculum details
status: {
type: Number,
default: 1, // Default status as active (1)
},
const commonFields = {
createdBy: String,
updatedBy: String,
createdAt: {
......@@ -28,6 +11,31 @@ const curriculumSchema = new mongoose.Schema({
type: Date,
default: new Date(),
},
};
const curriculumSchema = new mongoose.Schema({
curriculumCode: {
type: String,
unique: true, // Ensures unique values for curriculumCode
},
curriculumLevel: Number,
curriculumTitle: String,
curriculumDescription: String,
curriculumImage: String,
curriculumMark: {
type: Number,
default: 0
},
tutorials: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Tutorial",
}],
// Additional fields for curriculum details
status: {
type: Number,
default: 1, // Default status as active (1)
},
...commonFields
});
const Curriculum = mongoose.model("Curriculum", curriculumSchema);
......
import mongoose from "mongoose";
const commonFields = {
createdBy: String,
updatedBy: String,
createdAt: {
type: Date,
default: new Date(),
},
updatedAt: {
type: Date,
default: new Date(),
},
};
const taskItemSchema = new mongoose.Schema({
title: String,
description: String,
howToDo: String,
referenceImage: String,
referenceVideo: String,
taskItemMark : {
type: Number,
default: 0
}
// Additional fields for task items
});
const tutorialSchema = new mongoose.Schema({
tutorialCode: String,
tutorialCode: {
type: String,
unique: true, // Ensures unique values for tutorialCode
},
tutorialTitle: String,
tutorialDescription: String,
tutorialImage: String,
tutorialMarks: {
type: Number,
default: 0
},
taskItems: [taskItemSchema], // Embed task items as subdocuments
// Additional fields for tutorial details
status: {
type: Number,
default: 1, // Default status as active (1)
},
createdBy: String,
updatedBy: String,
createdAt: {
type: Date,
default: new Date(),
},
updatedAt: {
type: Date,
default: new Date(),
},
...commonFields
});
const Tutorial = mongoose.model("Tutorial", tutorialSchema);
......
import mongoose from "mongoose";
const taskItemSchema = new mongoose.Schema({
title: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
howToDo: [String],
referenceImage: String,
referenceVideo: String,
taskItemMark: {
type: Number,
default: 0
},
taskItemMarkUser: {
type: Number,
default: 0
},
taskItemSpentTime: {
type: Number,
default: 0
},
}, { _id: false });
const tutorialTypeUserProgressSchema = new mongoose.Schema({
tutorialCode: String,
tutorialTitle: String,
tutorialDescription: String,
tutorialImage: String,
tutorialMark: {
type: Number,
default: 0
},
tutorialMarkUser: {
type: Number,
default: 0
},
tutorialSpentTime: {
type: Number,
default: 0
},
taskItems: [taskItemSchema],
}, { _id: false });
const curriculumTypeUserProgressSchema = new mongoose.Schema({
curriculumCode: String,
curriculumLevel: Number,
curriculumTitle: String,
curriculumDescription: String,
curriculumImage: String,
curriculumMark: {
type: Number,
default: 0
},
curriculumMarkUser: {
type: Number,
default: 0
},
curriculumSpentTime: {
type: Number,
default: 0
},
tutorials: [tutorialTypeUserProgressSchema],
}, { _id: false });
const userProgressSchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User' // Reference to the User model
ref: 'User'
},
curriculums: [curriculumTypeUserProgressSchema],
totalCurriculumsMarks: {
type: Number,
default: 0
},
totalCurriculumSpentTime: {
type: Number,
default: 0
},
status: {
type: Number,
default: 1,
},
createdBy: String,
createdAt: {
type: Date,
default: new Date(),
},
updatedBy: String,
updatedAt: {
type: Date,
default: new Date(),
},
curriculumId: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'Curriculum' // Reference to the Curriculum model
},
tutorialProgress: [
{
tutorialId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Tutorial' // Reference to the Tutorial model
},
completed: {
type: Boolean,
default: false
}
}
],
marks: {
type: Number,
default: 0
}
});
const UserProgress = mongoose.model("UserProgress", userProgressSchema);
......
......@@ -19,6 +19,8 @@
"multer": "^1.4.5-lts.1",
"nodemailer": "^6.9.1",
"nodemon": "^2.0.22",
"react-mic": "^12.4.6",
"torch": "^0.2.7",
"uuid": "^9.0.0"
}
},
......@@ -58,6 +60,22 @@
"node": ">= 0.6"
}
},
"node_modules/ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/ansi-styles": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
"integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
......@@ -190,6 +208,29 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/chalk": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
"dependencies": {
"ansi-styles": "^2.2.1",
"escape-string-regexp": "^1.0.2",
"has-ansi": "^2.0.0",
"strip-ansi": "^3.0.0",
"supports-color": "^2.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/chalk/node_modules/supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
"integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/chokidar": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
......@@ -343,6 +384,14 @@
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
},
"node_modules/escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
......@@ -526,6 +575,17 @@
"node": ">= 0.4.0"
}
},
"node_modules/has-ansi": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
"integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==",
"dependencies": {
"ansi-regex": "^2.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
......@@ -637,6 +697,11 @@
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"node_modules/jsonwebtoken": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz",
......@@ -689,6 +754,17 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
},
"bin": {
"loose-envify": "cli.js"
}
},
"node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
......@@ -1058,6 +1134,16 @@
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.13.1"
}
},
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
......@@ -1119,6 +1205,47 @@
"node": ">= 0.8"
}
},
"node_modules/react": {
"version": "16.14.0",
"resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz",
"integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-ga": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/react-ga/-/react-ga-2.7.0.tgz",
"integrity": "sha512-AjC7UOZMvygrWTc2hKxTDvlMXEtbmA0IgJjmkhgmQQ3RkXrWR11xEagLGFGaNyaPnmg24oaIiaNPnEoftUhfXA==",
"peerDependencies": {
"prop-types": "^15.6.0",
"react": "^15.6.2 || ^16.0"
}
},
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/react-mic": {
"version": "12.4.6",
"resolved": "https://registry.npmjs.org/react-mic/-/react-mic-12.4.6.tgz",
"integrity": "sha512-2/DZoz7thR2nJyekF10zBvs/7a8HhUQ4L8MV6BpC+Q/T8G1MvpRHGSHjSlVtnbzaCMDJ3R1MdThoLu15WuVh/g==",
"dependencies": {
"prop-types": "^15.5.10",
"react-ga": "^2.2.0"
},
"peerDependencies": {
"prop-types": "^15.5.10",
"react": "16.x"
}
},
"node_modules/readable-stream": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
......@@ -1343,6 +1470,17 @@
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
"dependencies": {
"ansi-regex": "^2.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
......@@ -1373,6 +1511,18 @@
"node": ">=0.6"
}
},
"node_modules/torch": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/torch/-/torch-0.2.7.tgz",
"integrity": "sha512-yTv7qWKGg00hMDv0pyBgRjubbf4eygzzrjKPKRC9rbPCKBF0jd+cxnzIoN+pCHgGf2EQbd0jGyy1X7h5BIqjEA==",
"deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
"dependencies": {
"chalk": "^1.1.3"
},
"engines": {
"node": ">= 0.8.4"
}
},
"node_modules/touch": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
......@@ -1522,6 +1672,16 @@
"negotiator": "0.6.3"
}
},
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA=="
},
"ansi-styles": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
"integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA=="
},
"anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
......@@ -1629,6 +1789,25 @@
"get-intrinsic": "^1.0.2"
}
},
"chalk": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
"requires": {
"ansi-styles": "^2.2.1",
"escape-string-regexp": "^1.0.2",
"has-ansi": "^2.0.0",
"strip-ansi": "^3.0.0",
"supports-color": "^2.0.0"
},
"dependencies": {
"supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
"integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g=="
}
}
},
"chokidar": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
......@@ -1743,6 +1922,11 @@
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="
},
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
......@@ -1887,6 +2071,14 @@
"function-bind": "^1.1.1"
}
},
"has-ansi": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
"integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==",
"requires": {
"ansi-regex": "^2.0.0"
}
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
......@@ -1968,6 +2160,11 @@
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
},
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"jsonwebtoken": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz",
......@@ -2015,6 +2212,14 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"requires": {
"js-tokens": "^3.0.0 || ^4.0.0"
}
},
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
......@@ -2269,6 +2474,16 @@
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"requires": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.13.1"
}
},
"proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
......@@ -2312,6 +2527,37 @@
"unpipe": "1.0.0"
}
},
"react": {
"version": "16.14.0",
"resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz",
"integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==",
"peer": true,
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2"
}
},
"react-ga": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/react-ga/-/react-ga-2.7.0.tgz",
"integrity": "sha512-AjC7UOZMvygrWTc2hKxTDvlMXEtbmA0IgJjmkhgmQQ3RkXrWR11xEagLGFGaNyaPnmg24oaIiaNPnEoftUhfXA==",
"requires": {}
},
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"react-mic": {
"version": "12.4.6",
"resolved": "https://registry.npmjs.org/react-mic/-/react-mic-12.4.6.tgz",
"integrity": "sha512-2/DZoz7thR2nJyekF10zBvs/7a8HhUQ4L8MV6BpC+Q/T8G1MvpRHGSHjSlVtnbzaCMDJ3R1MdThoLu15WuVh/g==",
"requires": {
"prop-types": "^15.5.10",
"react-ga": "^2.2.0"
}
},
"readable-stream": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
......@@ -2489,6 +2735,14 @@
}
}
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
"requires": {
"ansi-regex": "^2.0.0"
}
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
......@@ -2510,6 +2764,14 @@
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
},
"torch": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/torch/-/torch-0.2.7.tgz",
"integrity": "sha512-yTv7qWKGg00hMDv0pyBgRjubbf4eygzzrjKPKRC9rbPCKBF0jd+cxnzIoN+pCHgGf2EQbd0jGyy1X7h5BIqjEA==",
"requires": {
"chalk": "^1.1.3"
}
},
"touch": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
......
......@@ -21,6 +21,8 @@
"multer": "^1.4.5-lts.1",
"nodemailer": "^6.9.1",
"nodemon": "^2.0.22",
"react-mic": "^12.4.6",
"torch": "^0.2.7",
"uuid": "^9.0.0"
}
}
import sys
import io
import base64
import torch
import torchvision.transforms as transforms
from PIL import Image
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
class theCNN(nn.Module):
def __init__(self):
super().__init__()
self.conv01 = nn.Conv2d(
in_channels=1,
out_channels=10,
kernel_size=5,
stride=1,
padding=1
)
self.dropout1 = nn.Dropout(0.2) # Add dropout layer
self.conv02 = nn.Conv2d(
in_channels=10,
out_channels=20,
kernel_size=5,
stride=1,
padding=1
)
self.dropout2 = nn.Dropout(0.2) # Add dropout layer
expectedSize = np.floor((73+2*0-1)/1) + 1
expectedSize = 20*int(expectedSize**2)
self.fc01 = nn.Linear(expectedSize, 50)
self.output = nn.Linear(50, 16)
def forward(self, x):
# convo -> maxpool -> relu
x = F.relu(F.max_pool2d(self.conv01(x), 2))
# convo -> maxpool -> relu
x = F.relu(F.max_pool2d(self.conv02(x), 2))
nUnits = x.shape.numel()/x.shape[0]
x = x.view(-1, int(nUnits))
x = F.relu(self.fc01(x))
return torch.softmax(self.output(x), axis=1)
# Load the mapping dictionary
class_mapping = {
0: 'Eight',
1: 'Eleven',
2: 'Fifty',
3: 'Five',
4: 'Four',
5: 'Fourteen',
6: 'Nine',
7: 'One',
8: 'Seven',
9: 'Six',
10: 'Ten',
11: 'Thirteen',
12: 'Thirty',
13: 'Three',
14: 'Twenty',
15: 'Two'
}
# Load the trained model
model = theCNN() # Instantiate your model class
model.load_state_dict(torch.load(
'D:/SLIIT_Y4_Research_Module/Research/Project/2023-029/Project/Backend/Server_Node/prediction_config/C1T1/trained_model_modified.pth', map_location=torch.device('cpu')))
model.eval()
# Read image data and target class from standard input
input_data = sys.stdin.readlines()
image_data = input_data[0].strip().encode() # Convert to bytes
target_class = input_data[1].strip()
# Preprocess the image
# image_data = base64.b64decode(sys.argv[1])
# image = Image.open(io.BytesIO(image_data))
image = Image.open(io.BytesIO(base64.b64decode(image_data)))
transform = transforms.Compose([
transforms.Resize((300, 300)),
transforms.Grayscale(num_output_channels=1),
transforms.ToTensor(),
transforms.Normalize(mean=[0.5], std=[0.5]),
])
image_tensor = transform(image).unsqueeze(0)
# Predict the class
with torch.no_grad():
outputs = model(image_tensor)
predicted_class = torch.argmax(outputs[0]).item()
confidence = outputs[0][predicted_class].item()
# Get the predicted class name using the mapping dictionary
predicted_class_name = class_mapping[predicted_class]
# Calculate similarity or confidence percentage
# target_class = sys.argv[2].lower() # Class name passed from the API
similarity = 100 * confidence
print(f"{predicted_class_name},{similarity:.2f}")
import express from "express";
import multer from "multer";
import { marksCalculator } from "../controllers/marksCalculator.controller.js";
// Set up storage for uploaded images
const storage = multer.memoryStorage();
const upload = multer({ storage: storage });
const router = express.Router();
router.post('/curriculum/:curriculumIndex/tutorial/:tutorialIndex', upload.single('image'), marksCalculator)
export default router;
\ No newline at end of file
import express from "express";
import { getUserProgress, updateUserProgress } from "../controllers/userProgress.controller.js";
import { getUserProgress, subscribeCurriculum, updateTaskItemProgress } from "../controllers/userProgress.controller.js";
const router = express.Router();
router.post('/subscribe-curriculum', subscribeCurriculum);
router.get('/:userId', getUserProgress);
router.post('/:userId', updateUserProgress);
router.put('/update-task-item-progress', updateTaskItemProgress);
export default router;
......@@ -4,10 +4,17 @@ import dotenv from "dotenv";
import express from "express";
import mongoose from "mongoose";
import multer from "multer";
// Set up storage for uploaded images
const storage = multer.memoryStorage();
const upload = multer({ storage: storage });
//import routes
import curriculumRoutes from "./routes/curriculum.routes.js";
import feedbackRoutes from "./routes/feedback.routes.js";
import leaderboardRoutes from "./routes/leaderboard.routes.js";
import marksCalculatorRoutes from "./routes/marksCalculator.routes.js";
import translateRoutes from "./routes/translate.routes.js";
import tutorialRoutes from "./routes/tutorial.routes.js";
import userRoutes from "./routes/user.routes.js";
......@@ -33,6 +40,7 @@ app.use("/rest_node/tutorial", tutorialRoutes);
app.use("/rest_node/user-progress", userProgressRoutes);
app.use("/rest_node/feedback", feedbackRoutes);
app.use("/rest_node/leaderboard", leaderboardRoutes);
app.use("/rest_node/marks-calculator", marksCalculatorRoutes);
const CONNECTION_URL = `mongodb+srv://${process.env.DB_USERNAME}:${process.env.DB_PASSWORD}@researchmanagement-appl.vzhn4.mongodb.net/?retryWrites=true&w=majority`;
const PORT = process.env.PORT || 5000;
......
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@mongodb-js/saslprep@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@mongodb-js/saslprep/-/saslprep-1.1.0.tgz#022fa36620a7287d17acd05c4aae1e5f390d250d"
integrity sha512-Xfijy7HvfzzqiOAhAepF4SGN5e9leLkMvg/OPOF97XemjfVCYN/oWa75wnkc6mltMSTwY+XlbhWgUOJmkFspSw==
dependencies:
sparse-bitfield "^3.0.3"
"@types/node@*":
version "20.5.7"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.5.7.tgz#4b8ecac87fbefbc92f431d09c30e176fc0a7c377"
integrity sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA==
"@types/webidl-conversions@*":
version "7.0.0"
resolved "https://registry.yarnpkg.com/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz#2b8e60e33906459219aa587e9d1a612ae994cfe7"
integrity sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog==
"@types/whatwg-url@^8.2.1":
version "8.2.2"
resolved "https://registry.yarnpkg.com/@types/whatwg-url/-/whatwg-url-8.2.2.tgz#749d5b3873e845897ada99be4448041d4cc39e63"
integrity sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==
dependencies:
"@types/node" "*"
"@types/webidl-conversions" "*"
abbrev@1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
accepts@~1.3.8:
version "1.3.8"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
dependencies:
mime-types "~2.1.34"
negotiator "0.6.3"
ansi-regex@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==
ansi-styles@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
integrity sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==
anymatch@~3.1.2:
version "3.1.3"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
dependencies:
normalize-path "^3.0.0"
picomatch "^2.0.4"
append-field@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56"
integrity sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==
array-flatten@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==
balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
bcryptjs@^2.4.3:
version "2.4.3"
resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb"
integrity sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==
binary-extensions@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
body-parser@1.20.1:
version "1.20.1"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668"
integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==
dependencies:
bytes "3.1.2"
content-type "~1.0.4"
debug "2.6.9"
depd "2.0.0"
destroy "1.2.0"
http-errors "2.0.0"
iconv-lite "0.4.24"
on-finished "2.4.1"
qs "6.11.0"
raw-body "2.5.1"
type-is "~1.6.18"
unpipe "1.0.0"
body-parser@^1.20.2:
version "1.20.2"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd"
integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==
dependencies:
bytes "3.1.2"
content-type "~1.0.5"
debug "2.6.9"
depd "2.0.0"
destroy "1.2.0"
http-errors "2.0.0"
iconv-lite "0.4.24"
on-finished "2.4.1"
qs "6.11.0"
raw-body "2.5.2"
type-is "~1.6.18"
unpipe "1.0.0"
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
braces@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
dependencies:
fill-range "^7.0.1"
bson@^5.4.0:
version "5.4.0"
resolved "https://registry.yarnpkg.com/bson/-/bson-5.4.0.tgz#0eea77276d490953ad8616b483298dbff07384c6"
integrity sha512-WRZ5SQI5GfUuKnPTNmAYPiKIof3ORXAF4IRU5UcgmivNIon01rWQlw5RUH954dpu8yGL8T59YShVddIPaU/gFA==
buffer-equal-constant-time@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==
buffer-from@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
busboy@^1.0.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893"
integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==
dependencies:
streamsearch "^1.1.0"
bytes@3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
call-bind@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==
dependencies:
function-bind "^1.1.1"
get-intrinsic "^1.0.2"
chalk@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
integrity sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==
dependencies:
ansi-styles "^2.2.1"
escape-string-regexp "^1.0.2"
has-ansi "^2.0.0"
strip-ansi "^3.0.0"
supports-color "^2.0.0"
chokidar@^3.5.2:
version "3.5.3"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
dependencies:
anymatch "~3.1.2"
braces "~3.0.2"
glob-parent "~5.1.2"
is-binary-path "~2.1.0"
is-glob "~4.0.1"
normalize-path "~3.0.0"
readdirp "~3.6.0"
optionalDependencies:
fsevents "~2.3.2"
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
concat-stream@^1.5.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
dependencies:
buffer-from "^1.0.0"
inherits "^2.0.3"
readable-stream "^2.2.2"
typedarray "^0.0.6"
content-disposition@0.5.4:
version "0.5.4"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
dependencies:
safe-buffer "5.2.1"
content-type@~1.0.4, content-type@~1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
cookie-signature@1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==
cookie@0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
core-util-is@~1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
cors@^2.8.5:
version "2.8.5"
resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29"
integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==
dependencies:
object-assign "^4"
vary "^1"
debug@2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
dependencies:
ms "2.0.0"
debug@4.x:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"
debug@^3.2.7:
version "3.2.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
dependencies:
ms "^2.1.1"
depd@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
destroy@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
dotenv@^16.0.3:
version "16.3.1"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e"
integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==
ecdsa-sig-formatter@1.0.11:
version "1.0.11"
resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf"
integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
dependencies:
safe-buffer "^5.0.1"
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
escape-html@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
escape-string-regexp@^1.0.2:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
etag@~1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
express@^4.18.2:
version "4.18.2"
resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59"
integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==
dependencies:
accepts "~1.3.8"
array-flatten "1.1.1"
body-parser "1.20.1"
content-disposition "0.5.4"
content-type "~1.0.4"
cookie "0.5.0"
cookie-signature "1.0.6"
debug "2.6.9"
depd "2.0.0"
encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
finalhandler "1.2.0"
fresh "0.5.2"
http-errors "2.0.0"
merge-descriptors "1.0.1"
methods "~1.1.2"
on-finished "2.4.1"
parseurl "~1.3.3"
path-to-regexp "0.1.7"
proxy-addr "~2.0.7"
qs "6.11.0"
range-parser "~1.2.1"
safe-buffer "5.2.1"
send "0.18.0"
serve-static "1.15.0"
setprototypeof "1.2.0"
statuses "2.0.1"
type-is "~1.6.18"
utils-merge "1.0.1"
vary "~1.1.2"
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
dependencies:
to-regex-range "^5.0.1"
finalhandler@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32"
integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==
dependencies:
debug "2.6.9"
encodeurl "~1.0.2"
escape-html "~1.0.3"
on-finished "2.4.1"
parseurl "~1.3.3"
statuses "2.0.1"
unpipe "~1.0.0"
forwarded@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
fresh@0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==
fsevents@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
get-intrinsic@^1.0.2:
version "1.2.1"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82"
integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==
dependencies:
function-bind "^1.1.1"
has "^1.0.3"
has-proto "^1.0.1"
has-symbols "^1.0.3"
glob-parent@~5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
dependencies:
is-glob "^4.0.1"
has-ansi@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
integrity sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==
dependencies:
ansi-regex "^2.0.0"
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==
has-proto@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0"
integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==
has-symbols@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
has@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
dependencies:
function-bind "^1.1.1"
http-errors@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==
dependencies:
depd "2.0.0"
inherits "2.0.4"
setprototypeof "1.2.0"
statuses "2.0.1"
toidentifier "1.0.1"
iconv-lite@0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
dependencies:
safer-buffer ">= 2.1.2 < 3"
ignore-by-default@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09"
integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==
inherits@2.0.4, inherits@^2.0.3, inherits@~2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
ip@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da"
integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==
ipaddr.js@1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
is-binary-path@~2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
dependencies:
binary-extensions "^2.0.0"
is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
is-glob@^4.0.1, is-glob@~4.0.1:
version "4.0.3"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
dependencies:
is-extglob "^2.1.1"
is-number@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==
"js-tokens@^3.0.0 || ^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
jsonwebtoken@^9.0.0:
version "9.0.1"
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz#81d8c901c112c24e497a55daf6b2be1225b40145"
integrity sha512-K8wx7eJ5TPvEjuiVSkv167EVboBDv9PZdDoF7BgeQnBLVvZWW9clr2PsQHVJDTKaEIH5JBIwHujGcHp7GgI2eg==
dependencies:
jws "^3.2.2"
lodash "^4.17.21"
ms "^2.1.1"
semver "^7.3.8"
jwa@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a"
integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==
dependencies:
buffer-equal-constant-time "1.0.1"
ecdsa-sig-formatter "1.0.11"
safe-buffer "^5.0.1"
jws@^3.2.2:
version "3.2.2"
resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304"
integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==
dependencies:
jwa "^1.4.1"
safe-buffer "^5.0.1"
kareem@2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/kareem/-/kareem-2.5.1.tgz#7b8203e11819a8e77a34b3517d3ead206764d15d"
integrity sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==
lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
dependencies:
js-tokens "^3.0.0 || ^4.0.0"
lru-cache@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
dependencies:
yallist "^4.0.0"
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
memory-pager@^1.0.2:
version "1.5.0"
resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5"
integrity sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==
merge-descriptors@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==
methods@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==
mime-db@1.52.0:
version "1.52.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-types@~2.1.24, mime-types@~2.1.34:
version "2.1.35"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
mime@1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
minimatch@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
dependencies:
brace-expansion "^1.1.7"
minimist@^1.2.6:
version "1.2.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
mkdirp@^0.5.4:
version "0.5.6"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6"
integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==
dependencies:
minimist "^1.2.6"
mongodb-connection-string-url@^2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz#57901bf352372abdde812c81be47b75c6b2ec5cf"
integrity sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==
dependencies:
"@types/whatwg-url" "^8.2.1"
whatwg-url "^11.0.0"
mongodb@5.8.1:
version "5.8.1"
resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-5.8.1.tgz#dc201adfbd6c6d73401cdcf12ebdb75f14771faf"
integrity sha512-wKyh4kZvm6NrCPH8AxyzXm3JBoEf4Xulo0aUWh3hCgwgYJxyQ1KLST86ZZaSWdj6/kxYUA3+YZuyADCE61CMSg==
dependencies:
bson "^5.4.0"
mongodb-connection-string-url "^2.6.0"
socks "^2.7.1"
optionalDependencies:
"@mongodb-js/saslprep" "^1.1.0"
mongoose@^7.1.1:
version "7.5.0"
resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-7.5.0.tgz#d10003ffc1ff876d761c7cbca6844ddd2aadd42f"
integrity sha512-FpOWOb0AJuaVcplmEyIJ2eCbVGe4gOoniPD+pmft5BrGrNrsFcnYXlERdXtBApGHMHPwD7WbxTyhCbUNr72F3Q==
dependencies:
bson "^5.4.0"
kareem "2.5.1"
mongodb "5.8.1"
mpath "0.9.0"
mquery "5.0.0"
ms "2.1.3"
sift "16.0.1"
mpath@0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/mpath/-/mpath-0.9.0.tgz#0c122fe107846e31fc58c75b09c35514b3871904"
integrity sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==
mquery@5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/mquery/-/mquery-5.0.0.tgz#a95be5dfc610b23862df34a47d3e5d60e110695d"
integrity sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==
dependencies:
debug "4.x"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
ms@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
ms@2.1.3, ms@^2.1.1:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
multer@^1.4.5-lts.1:
version "1.4.5-lts.1"
resolved "https://registry.yarnpkg.com/multer/-/multer-1.4.5-lts.1.tgz#803e24ad1984f58edffbc79f56e305aec5cfd1ac"
integrity sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==
dependencies:
append-field "^1.0.0"
busboy "^1.0.0"
concat-stream "^1.5.2"
mkdirp "^0.5.4"
object-assign "^4.1.1"
type-is "^1.6.4"
xtend "^4.0.0"
negotiator@0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
nodemailer@^6.9.1:
version "6.9.4"
resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.9.4.tgz#93bd4a60eb0be6fa088a0483340551ebabfd2abf"
integrity sha512-CXjQvrQZV4+6X5wP6ZIgdehJamI63MFoYFGGPtHudWym9qaEHDNdPzaj5bfMCvxG1vhAileSWW90q7nL0N36mA==
nodemon@^2.0.22:
version "2.0.22"
resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.22.tgz#182c45c3a78da486f673d6c1702e00728daf5258"
integrity sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==
dependencies:
chokidar "^3.5.2"
debug "^3.2.7"
ignore-by-default "^1.0.1"
minimatch "^3.1.2"
pstree.remy "^1.1.8"
semver "^5.7.1"
simple-update-notifier "^1.0.7"
supports-color "^5.5.0"
touch "^3.1.0"
undefsafe "^2.0.5"
nopt@~1.0.10:
version "1.0.10"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee"
integrity sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==
dependencies:
abbrev "1"
normalize-path@^3.0.0, normalize-path@~3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
object-assign@^4, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
object-inspect@^1.9.0:
version "1.12.3"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9"
integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==
on-finished@2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f"
integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==
dependencies:
ee-first "1.1.1"
parseurl@~1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
path-to-regexp@0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==
picomatch@^2.0.4, picomatch@^2.2.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
prop-types@^15.5.10:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
dependencies:
loose-envify "^1.4.0"
object-assign "^4.1.1"
react-is "^16.13.1"
proxy-addr@~2.0.7:
version "2.0.7"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==
dependencies:
forwarded "0.2.0"
ipaddr.js "1.9.1"
pstree.remy@^1.1.8:
version "1.1.8"
resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a"
integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==
punycode@^2.1.1:
version "2.3.0"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f"
integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==
qs@6.11.0:
version "6.11.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a"
integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==
dependencies:
side-channel "^1.0.4"
range-parser@~1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
raw-body@2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857"
integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==
dependencies:
bytes "3.1.2"
http-errors "2.0.0"
iconv-lite "0.4.24"
unpipe "1.0.0"
raw-body@2.5.2:
version "2.5.2"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a"
integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==
dependencies:
bytes "3.1.2"
http-errors "2.0.0"
iconv-lite "0.4.24"
unpipe "1.0.0"
react-ga@^2.2.0:
version "2.7.0"
resolved "https://registry.yarnpkg.com/react-ga/-/react-ga-2.7.0.tgz#24328f157f31e8cffbf4de74a3396536679d8d7c"
integrity sha512-AjC7UOZMvygrWTc2hKxTDvlMXEtbmA0IgJjmkhgmQQ3RkXrWR11xEagLGFGaNyaPnmg24oaIiaNPnEoftUhfXA==
react-is@^16.13.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-mic@^12.4.6:
version "12.4.6"
resolved "https://registry.yarnpkg.com/react-mic/-/react-mic-12.4.6.tgz#e66ca4e1074ebfd26f6972f26ba2d99d85fd3bc2"
integrity sha512-2/DZoz7thR2nJyekF10zBvs/7a8HhUQ4L8MV6BpC+Q/T8G1MvpRHGSHjSlVtnbzaCMDJ3R1MdThoLu15WuVh/g==
dependencies:
prop-types "^15.5.10"
react-ga "^2.2.0"
readable-stream@^2.2.2:
version "2.3.8"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b"
integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.3"
isarray "~1.0.0"
process-nextick-args "~2.0.0"
safe-buffer "~5.1.1"
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
readdirp@~3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
dependencies:
picomatch "^2.2.1"
safe-buffer@5.2.1, safe-buffer@^5.0.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
"safer-buffer@>= 2.1.2 < 3":
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
semver@^5.7.1:
version "5.7.2"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8"
integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==
semver@^7.3.8:
version "7.5.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
dependencies:
lru-cache "^6.0.0"
semver@~7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
send@0.18.0:
version "0.18.0"
resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be"
integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==
dependencies:
debug "2.6.9"
depd "2.0.0"
destroy "1.2.0"
encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
fresh "0.5.2"
http-errors "2.0.0"
mime "1.6.0"
ms "2.1.3"
on-finished "2.4.1"
range-parser "~1.2.1"
statuses "2.0.1"
serve-static@1.15.0:
version "1.15.0"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540"
integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==
dependencies:
encodeurl "~1.0.2"
escape-html "~1.0.3"
parseurl "~1.3.3"
send "0.18.0"
setprototypeof@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
side-channel@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==
dependencies:
call-bind "^1.0.0"
get-intrinsic "^1.0.2"
object-inspect "^1.9.0"
sift@16.0.1:
version "16.0.1"
resolved "https://registry.yarnpkg.com/sift/-/sift-16.0.1.tgz#e9c2ccc72191585008cf3e36fc447b2d2633a053"
integrity sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==
simple-update-notifier@^1.0.7:
version "1.1.0"
resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz#67694c121de354af592b347cdba798463ed49c82"
integrity sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==
dependencies:
semver "~7.0.0"
smart-buffer@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae"
integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==
socks@^2.7.1:
version "2.7.1"
resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55"
integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==
dependencies:
ip "^2.0.0"
smart-buffer "^4.2.0"
sparse-bitfield@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz#ff4ae6e68656056ba4b3e792ab3334d38273ca11"
integrity sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==
dependencies:
memory-pager "^1.0.2"
statuses@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
streamsearch@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764"
integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
dependencies:
safe-buffer "~5.1.0"
strip-ansi@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==
dependencies:
ansi-regex "^2.0.0"
supports-color@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
integrity sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==
supports-color@^5.5.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
dependencies:
has-flag "^3.0.0"
to-regex-range@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
dependencies:
is-number "^7.0.0"
toidentifier@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
torch@^0.2.7:
version "0.2.7"
resolved "https://registry.yarnpkg.com/torch/-/torch-0.2.7.tgz#ac4bc487e988ee582b383ece48fe0169e95443ad"
integrity sha512-yTv7qWKGg00hMDv0pyBgRjubbf4eygzzrjKPKRC9rbPCKBF0jd+cxnzIoN+pCHgGf2EQbd0jGyy1X7h5BIqjEA==
dependencies:
chalk "^1.1.3"
touch@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b"
integrity sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==
dependencies:
nopt "~1.0.10"
tr46@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9"
integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==
dependencies:
punycode "^2.1.1"
type-is@^1.6.4, type-is@~1.6.18:
version "1.6.18"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
dependencies:
media-typer "0.3.0"
mime-types "~2.1.24"
typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==
undefsafe@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c"
integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
utils-merge@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
uuid@^9.0.0:
version "9.0.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5"
integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==
vary@^1, vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
webidl-conversions@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a"
integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==
whatwg-url@^11.0.0:
version "11.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018"
integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==
dependencies:
tr46 "^3.0.0"
webidl-conversions "^7.0.0"
xtend@^4.0.0:
version "4.0.2"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
yallist@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
......@@ -16,6 +16,9 @@
files/*
!files/
*.pyc
*~
*.swp
# Created by https://www.toptal.com/developers/gitignore/api/python
# Edit at https://www.toptal.com/developers/gitignore?templates=python
......
......@@ -4,3 +4,36 @@
2023-05-19 00:32:48,522 - ERROR - Received request at root endpoint.
2023-05-19 23:09:38,565 - INFO - Failed to make predictions. name 'CLASSES' is not defined
2023-05-19 23:09:38,565 - INFO - Failed to make predictions. name 'CLASSES' is not defined
2023-05-24 20:05:37,932 - INFO - Failed to make predictions. OpenCV(4.7.0) D:\a\opencv-python\opencv-python\opencv\modules\imgproc\src\color.cpp:182: error: (-215:Assertion failed) !_src.empty() in function 'cv::cvtColor'
2023-05-24 20:05:37,932 - INFO - Failed to make predictions. OpenCV(4.7.0) D:\a\opencv-python\opencv-python\opencv\modules\imgproc\src\color.cpp:182: error: (-215:Assertion failed) !_src.empty() in function 'cv::cvtColor'
2023-05-24 20:05:37,932 - INFO - Failed to make predictions. OpenCV(4.7.0) D:\a\opencv-python\opencv-python\opencv\modules\imgproc\src\color.cpp:182: error: (-215:Assertion failed) !_src.empty() in function 'cv::cvtColor'
2023-05-24 20:05:37,936 - INFO - Error.
2023-05-24 20:05:37,936 - INFO - Error.
2023-05-24 20:05:37,936 - INFO - Error.
2023-07-12 05:50:25,202 - INFO - Error. 'SignLanguagePredictionService' object has no attribute 'predict_sign_language_video2'
2023-07-12 05:50:25,202 - INFO - Error. 'SignLanguagePredictionService' object has no attribute 'predict_sign_language_video2'
2023-07-12 05:50:25,202 - INFO - Error. 'SignLanguagePredictionService' object has no attribute 'predict_sign_language_video2'
2023-07-12 06:33:48,435 - INFO - Error. SignLanguagePredictionService.predict_sign_language_video_new2() missing 1 required positional argument: 'speed'
2023-07-12 06:33:48,435 - INFO - Error. SignLanguagePredictionService.predict_sign_language_video_new2() missing 1 required positional argument: 'speed'
2023-07-12 06:33:48,435 - INFO - Error. SignLanguagePredictionService.predict_sign_language_video_new2() missing 1 required positional argument: 'speed'
2023-07-12 06:34:27,777 - INFO - Error. SignLanguagePredictionService.predict_sign_language_video_new2() missing 1 required positional argument: 'speed'
2023-07-12 06:34:27,777 - INFO - Error. SignLanguagePredictionService.predict_sign_language_video_new2() missing 1 required positional argument: 'speed'
2023-07-12 06:34:27,777 - INFO - Error. SignLanguagePredictionService.predict_sign_language_video_new2() missing 1 required positional argument: 'speed'
2023-07-12 06:34:33,502 - INFO - Error. SignLanguagePredictionService.predict_sign_language_video_new2() missing 1 required positional argument: 'speed'
2023-07-12 06:34:33,502 - INFO - Error. SignLanguagePredictionService.predict_sign_language_video_new2() missing 1 required positional argument: 'speed'
2023-07-12 06:34:33,502 - INFO - Error. SignLanguagePredictionService.predict_sign_language_video_new2() missing 1 required positional argument: 'speed'
2023-07-12 06:39:07,178 - INFO - Failed to make predictions. local variable 'threshold_percentage' referenced before assignment
2023-07-12 06:39:07,178 - INFO - Failed to make predictions. local variable 'threshold_percentage' referenced before assignment
2023-07-12 06:39:07,178 - INFO - Failed to make predictions. local variable 'threshold_percentage' referenced before assignment
2023-07-12 06:39:07,180 - INFO - Error.
2023-07-12 06:39:07,180 - INFO - Error.
2023-07-12 06:39:07,180 - INFO - Error.
2023-08-03 06:00:41,581 - INFO - Failed to make predictions. [WinError 32] The process cannot access the file because it is being used by another process: 'files/test_video.mp4'
2023-08-03 06:00:41,581 - INFO - Failed to make predictions. [WinError 32] The process cannot access the file because it is being used by another process: 'files/test_video.mp4'
2023-08-03 06:00:41,581 - INFO - Failed to make predictions. [WinError 32] The process cannot access the file because it is being used by another process: 'files/test_video.mp4'
2023-08-03 06:00:41,626 - INFO - Error.
2023-08-03 06:00:41,626 - INFO - Error.
2023-08-03 06:00:41,626 - INFO - Error.
import base64
import os
import cv2
from fastapi import APIRouter, File, HTTPException, UploadFile
import numpy as np
from fastapi import APIRouter, File, HTTPException, Query,UploadFile
from pydantic import BaseModel
import tensorflow as tf
from core.logger import setup_logger
from core import setup_logger
from services.translate_service import SignLanguagePredictionService
from utils import mappings
......@@ -21,16 +18,17 @@ class ImageRequest(BaseModel):
# Load your Keras model
# model = tf.keras.models.load_model('../ML_Models/sign_language_to_text/models/sign_language_model.h5')
model = None
model= None
CLASSES = mappings.classes
NUM_CLASSES = len(mappings.classes) # number of classes
IMG_SIZE = 224 # image size
speed_levels = mappings.speed_levels
# Instantiate the service class
prediction_service = SignLanguagePredictionService(model, CLASSES, mappings)
prediction_service = SignLanguagePredictionService(model, CLASSES, mappings,speed_levels)
@router.post("/upload/video")
@router.post("/upload/video", tags=["Sign Language"])
async def upload_video(video: UploadFile = File(...)):
try:
file_location = f"files/{video.filename}"
......@@ -40,22 +38,36 @@ async def upload_video(video: UploadFile = File(...)):
return {"text": "ආආආආආආආ"}
except Exception as e:
logger.info(f"Failed to upload file. {e}")
raise HTTPException(status_code=500, detail="Failed to upload the video")
@router.post("/predict-sign-language/image")
raise HTTPException(
status_code=500,
detail="Failed to upload the video"
)
@router.post('/predict-sign-language/image', tags=["Sign Language"])
def predict_using_image(image_request: UploadFile = File(...)):
try:
return prediction_service.predict_sign_language(image_request)
except Exception as e:
logger.info(f"Error. {e}")
raise HTTPException(status_code=500, detail="Request Failed.")
@router.post("/predict-sign-language/video")
raise HTTPException(
status_code=500,
detail="Request Failed."
)
@router.post('/predict-sign-language/video', tags=["Sign Language"])
def predict_using_video(video_request: UploadFile = File(...)):
try:
return prediction_service.predict_sign_language_video(video_request)
return prediction_service.predict_sign_language_video_new(video_request)
except Exception as e:
logger.info(f"Error. {e}")
raise HTTPException(
status_code=500,
detail="Request Failed."
)
@router.post('/predict-sign-language/video/speed_levels', tags=["Sign Language"])
def predict_using_video(video_request: UploadFile = File(...), speed: int = Query(...)):
try:
return prediction_service.predict_sign_language_video_with_speed_levels(video_request, speed=speed)
except Exception as e:
logger.info(f"Error. {e}")
raise HTTPException(status_code=500, detail="Request Failed.")
......@@ -7,12 +7,12 @@ def test():
# Your code here
return {"pong"}
@router.get("/users")
@router.get("/test")
def get_users():
# Your code here
return {"message": "Get users endpoint"}
@router.post("/users")
@router.post("/test-api")
def create_user():
# Your code here
return {"message": "Create user endpoint"}
import logging
def setup_logger():
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
# Create a file handler for logging to a file
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(formatter)
# Create a stream handler for logging to console
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.INFO)
stream_handler.setFormatter(formatter)
# Add the handlers to the logger
logger.addHandler(file_handler)
logger.addHandler(stream_handler)
return logger
import logging
def setup_logger():
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
# Create a file handler for logging to a file
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(formatter)
# Create a stream handler for logging to console
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.INFO)
stream_handler.setFormatter(formatter)
# Add the handlers to the logger
logger.addHandler(file_handler)
logger.addHandler(stream_handler)
return logger
from controllers import (translate_controler, users_controller,
video_to_sign_language_controller)
from core import setup_logger
# from core import setup_logger
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from pymongo.mongo_client import MongoClient
from pymongo.mongo_client import MongoClient
from controllers import (translate_controler, users_controller, video_to_sign_language_controller)
# from controllers import translate_controler, users_controller
# from core.logger import setup_logger
app = FastAPI()
......@@ -31,7 +33,7 @@ async def shutdown_db_client():
app.mongodb_client.close()
logger = setup_logger()
# logger = setup_logger()
app.include_router(users_controller.router)
app.include_router(translate_controler.router)
......@@ -41,8 +43,9 @@ app.include_router(video_to_sign_language_controller.router)
origins = [
"http://localhost",
"http://localhost:8080",
"http://127.0.0.1:8000",
"http://localhost:3000"
"http://127.0.0.1:8000",
"http://localhost:8004",
"http://localhost:3000",
]
app.add_middleware(
......
......@@ -3,22 +3,21 @@ import cv2
import numpy as np
from fastapi import HTTPException, UploadFile
from typing import Dict
from typing import Counter, Dict
import tensorflow as tf
from core.logger import setup_logger
from core import setup_logger
from utils import mappings
logger = setup_logger()
IMG_SIZE = 224 # image size
class SignLanguagePredictionService:
def __init__(self, model, classes, mappings):
def __init__(self, model, classes, mappings,speed_levels):
self.model = model
self.classes = classes
self.mappings = mappings
self.speed_levels = speed_levels
def predict_sign_language(self, image_request: UploadFile) -> Dict[str, str]:
try:
......@@ -62,7 +61,7 @@ class SignLanguagePredictionService:
frame_count = 0
# Loop through the frames of the video
while frame_count < 20:
while frame_count < 50:
success, frame = video.read()
if not success:
break
......@@ -95,3 +94,150 @@ class SignLanguagePredictionService:
status_code=500,
detail="Failed to make predictions"
)
def predict_sign_language_video_new(self, video_request: UploadFile) -> Dict[str, str]:
try:
# Create a temporary file to save the video
video_location = f"files/{video_request.filename}"
with open(video_location, "wb") as file:
file.write(video_request.file.read())
# Read the video using OpenCV
video = cv2.VideoCapture(video_location)
predictions = []
frame_count = 0
# Loop through the frames of the video
while frame_count < 50:
success, frame = video.read()
if not success:
break
# Preprocess the frame
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame = cv2.resize(frame, (IMG_SIZE, IMG_SIZE))
frame = extract_hand_shape(frame)
frame = np.array([frame], dtype=np.float32) / 255.0
# Make prediction
prediction = self.model.predict(frame)
class_index = np.argmax(prediction)
class_name = self.classes[class_index]
sinhala_letter = self.mappings.letter_mapping.get(class_name, 'Unknown')
# Store the prediction for the frame
predictions.append(sinhala_letter)
frame_count += 1
video.release()
# Delete the video file
os.remove(video_location)
threshold_percentage = 60
predictions = get_predicted_percentage(predictions, threshold_percentage)
return {'frame_count': frame_count, 'predictions': predictions }
except Exception as e:
logger.info(f"Failed to make predictions. {e}")
raise HTTPException(
status_code=500,
detail="Failed to make predictions"
)
def predict_sign_language_video_with_speed_levels(self, video_request: UploadFile, speed: int) -> Dict[str, str]:
try:
# Create a temporary file to save the video
video_location = f"files/{video_request.filename}"
with open(video_location, "wb") as file:
file.write(video_request.file.read())
# Read the video using OpenCV
video = cv2.VideoCapture(video_location)
predictions = []
final_predictions = []
frame_count = 0
# Determine the number of frames per sign based on the speed level
frames_per_sign = self.speed_levels.get(speed, 50) # Default to level 1 if speed level is not provided
# Loop through the frames of the video
while frame_count <= video.get(cv2.CAP_PROP_FRAME_COUNT):
success, frame = video.read()
if not success:
break
# TODO Add to Config
if frame_count >= 500:
break
# Preprocess the frame
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame = cv2.resize(frame, (IMG_SIZE, IMG_SIZE))
frame = extract_hand_shape(frame)
frame = np.array([frame], dtype=np.float32) / 255.0
# Make prediction
prediction = self.model.predict(frame)
class_index = np.argmax(prediction)
class_name = self.classes[class_index]
sinhala_letter = self.mappings.letter_mapping.get(class_name, 'Unknown')
# Store the prediction for the frame
predictions.append(sinhala_letter)
frame_count += 1
# TODO Add to Config
threshold_percentage = 60
# Check if the required number of frames per sign has been reached
if frame_count % frames_per_sign == 0:
predictions = get_predicted_percentage(predictions, threshold_percentage)
final_predictions = final_predictions+predictions
predictions = []
video.release()
# Delete the video file
os.remove(video_location)
return {'frame_count': frame_count, 'predictions': final_predictions}
except Exception as e:
logger.info(f"Failed to make predictions. {e}")
raise HTTPException(
status_code=500,
detail="Failed to make predictions"
)
def extract_hand_shape(image):
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
_, thresholded = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
contours, _ = cv2.findContours(thresholded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = sorted(contours, key=cv2.contourArea, reverse=True)
hand_contour = contours[0]
hand_shape = np.zeros_like(image)
cv2.drawContours(hand_shape, [hand_contour], 0, (255, 255, 255), thickness=cv2.FILLED)
return hand_shape
def get_predicted_percentage(array, threshold):
counts = Counter(array)
total_elements = len(array)
percentages = {}
for element, count in counts.items():
percentage = (count / total_elements) * 100
percentages[element] = percentage
elements_above_threshold = [element for element, percentage in percentages.items() if percentage > threshold]
return elements_above_threshold
\ No newline at end of file
......@@ -27,4 +27,13 @@ classes =['A',
'Ohh',
'T',
'Uh',
'Uhh']
\ No newline at end of file
'Uhh']
speed_levels = {
1: 50, # 10 frames per sign for level 1
2: 40, # 20 frames per sign for level 2
3: 30, # 30 frames per sign for level 3
4: 20, # 40 frames per sign for level 4
5: 10 # 50 frames per sign for level 5
}
\ No newline at end of file
......@@ -7,12 +7,12 @@ Welcome to the FitsPro ERP Platform repository! This project aims to revolutioni
#### update packages
```
source-folder > yarn
source-folder > yarn
```
#### Start the project
#### Start the project
```
```
source-folder > yarn start
```
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -22,7 +22,9 @@
"@fullcalendar/timegrid": "^6.1.5",
"@fullcalendar/timeline": "^6.1.5",
"@hello-pangea/dnd": "^16.2.0",
"@material-ui/core": "^4.12.4",
"@mui/base": "^5.0.0-alpha.126",
"@mui/icons-material": "^5.14.6",
"@mui/lab": "^5.0.0-alpha.127",
"@mui/material": "^5.12.1",
"@mui/system": "^5.12.1",
......@@ -77,6 +79,7 @@
"react-infinite-scroll-component": "^6.1.0",
"react-intersection-observer": "^9.4.3",
"react-intl": "^6.4.0",
"react-material-file-upload": "^0.0.4",
"react-number-format": "^5.1.4",
"react-organizational-chart": "^2.2.1",
"react-quill": "^2.0.0",
......@@ -93,6 +96,7 @@
"react-table-sticky": "^1.1.3",
"react-timer-hook": "^3.0.5",
"react-to-print": "^2.14.12",
"react-webcam": "^7.1.1",
"react-window": "^1.8.9",
"react-zoom-pan-pinch": "^3.0.7",
"react18-input-otp": "^1.1.3",
......
export interface CurriculumLevelsType {
id: number
code: string
description: string
}
// ==============================|| DATA - CURRICULUM LEVELS ||============================== //
const curriculumLevels: readonly CurriculumLevelsType[] = [
{ id: 1, code: "Level 1", description: "Preliminary" },
{ id: 2, code: "Level 2", description: "Intermediate" },
{ id: 3, code: "Level 3", description: "Advance" },
];
export default curriculumLevels;
import { curriculumType } from "types/curriculum";
export const curriculums: curriculumType[] = [
{
"curriculumCode": "01",
"curriculumLevel": 1,
"curriculumDescription": "This curriculum teaches basic sign language skills to help beginners communicate effectively using sign language.",
"curriculumTitle": "Basic Sign Language Skills",
"curriculumImage": "https://drive.google.com/uc?export=view&id=1YACBlu7X-O7-DKv5DoW3AM9kgfT7Yhdc",
"_id": "1",
"status": 1,
"createdBy": "Nuwan Gamage",
"createdAt": new Date("2023-08-30T12:00:00Z"),
"curriculumMark": 100,
"tutorials": [
{
"tutorialCode": "01",
"tutorialTitle": "Numbers and Counting",
"tutorialDescription": "In this tutorial, you'll discover how to express numbers visually using simple hand gestures. Each number has a unique sign that involves specific finger placements and hand movements. We'll break down each number step by step, providing you with clear instructions, images, and videos to help you learn effectively.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1GeFzoy3xt8UnfCQE3IPVjPXoAg7GAWgf",
"_id": "1",
"status": 1,
"createdBy": "Nuwan Gamage",
"createdAt": new Date("2023-08-30T12:00:00Z"),
"tutorialMark": 100,
"taskItems": [
{
"_id": "1",
"title": "Learn Number One",
"description": "In this lesson, you will learn how to sign the number one. Understanding this basic sign is crucial for counting and expressing singular items or concepts in sign language. Practice the hand shape, movement, and facial expression associated with the sign to improve your fluency.",
"howToDo": [
"- Extend your index finger straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=17sHGfW9zip8xAwbRtUihzxkseKq-Qn7q",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "2",
"title": "Learn Number Two",
"description": "In this lesson, you will learn how to sign the number two. Mastering this sign is essential for counting and expressing pairs or dual concepts in sign language. Pay attention to the hand shape, movement, and facial expression involved in signing the number two to enhance your signing skills.",
"howToDo": [
"- Extend your index and middle fingers straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."],
"referenceImage": "https://drive.google.com/uc?export=view&id=1Nm-we15Rrr04ovRC1sr-ZVMNHbnALRm2",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "3",
"title": "Learn Number Three",
"description": "In this lesson, you will learn how to sign the number three in sign language. Mastering this sign is essential for expressing the quantity or position of three items. Pay attention to the hand shape, movement, and facial expression involved in signing the number three to enhance your signing skills.",
"howToDo": [
"- Extend your index, middle, and ring fingers straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1mKY8D1utbqm8flnNM7DZRKtuPQDXqFSm",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "4",
"title": "Learn Number Four",
"description": "In this lesson, you will learn how to sign the number four in sign language. This sign is commonly used for counting or indicating the quantity or position of four items. Focus on the hand shape, movement, and expression to accurately convey the number four in sign language.",
"howToDo": [
"- Extend your thumb, index, middle, and ring fingers straight up.",
"- Keep your pinky finger folded.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1VtbHehsxbOC04fVR6_Ca6HDv6csH17SJ",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "5",
"title": "Learn Number Five",
"description": "In this lesson, you will learn how to sign the number five in sign language. Mastering this sign is crucial for counting, expressing quantities, or representing the concept of five. Practice the hand shape, movement, and facial expression to effectively communicate the number five in sign language.",
"howToDo": [
"- Extend all your fingers straight up.",
"- Keep your thumb resting on the side of your palm.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1G6qx4dhHTKUPvNag0R3qlDJugNOgcTqM",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "6",
"title": "Learn Number Six",
"description": "In this lesson, you will learn how to sign the number six in sign language. This sign is commonly used for counting, indicating quantities, or representing the concept of six. Pay attention to the hand shape, movement, and expression involved in signing the number six to enhance your signing proficiency.",
"howToDo": [
"- Extend your thumb and pinky finger straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1Q2TbcPTx8KuLp4v2mPL_7GHO0l52DZXw",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "7",
"title": "Learn Number Seven",
"description": "In this lesson, you will learn how to sign the number seven in sign language. Mastering this sign is essential for counting, expressing quantities, or representing the concept of seven. Focus on the hand shape, movement, and facial expression to accurately convey the number seven in sign language.",
"howToDo": [
"- Extend your index, middle, and ring fingers straight up.",
"- Keep your thumb, pinky, and pinky finger folded.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1Jwt1TqXO1L7t3JFqQYzKL4S4GCuqULJ1",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "8",
"title": "Learn Number Eight",
"description": "In this lesson, you will learn how to sign the number eight in sign language. This sign is commonly used for counting, indicating quantities, or representing the concept of eight. Practice the hand shape, movement, and expression to effectively communicate the number eight in sign language.",
"howToDo": [
"- Extend all your fingers straight up.",
"- Cross your index and middle fingers over your ring and pinky fingers.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1zSf4hmqIUCcfebgoD6DTWmJ3NxY36LJL",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "9",
"title": "Learn Number Nine",
"description": "In this lesson, you will learn how to sign the number nine in sign language. Mastering this sign is crucial for counting, expressing quantities, or representing the concept of nine. Pay attention to the hand shape, movement, and expression involved in signing the number nine to enhance your signing proficiency.",
"howToDo": [
"- Extend your thumb and all your fingers straight up.",
"- Keep your pinky finger folded.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=16WB1Ifhq_ozKOO-ehfIqVRB6oC3B5STw",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "10",
"title": "Learn Number Ten",
"description": "In this lesson, you will learn how to sign the number ten in sign language. This sign is commonly used for counting, indicating quantities, or representing the concept of ten. Focus on the hand shape, movement, and facial expression to accurately convey the number ten in sign language.",
"howToDo": [
"- Extend your thumb, index, and middle fingers straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=11tCYFYjdVrr5LIFGyzOrIUg8bTURATZh",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
}
]
},
{
"tutorialCode": "02",
"tutorialTitle": "Everyday Vocabulary",
"tutorialDescription": "Teach signs for everyday objects and activities, such as eat, drink, sleep, book, pen, etc.\nIntroduce signs for common words used in daily life.\nProvide visual demonstrations and interactive exercises for learners to practice using these signs.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1QqmeBBiAojz7jaHUUdQGLyqUVR-mKSsy",
"taskItems": [],
"_id": "2",
"status": 1,
"createdBy": "Nuwan Gamage",
"createdAt": new Date("2023-08-30T12:00:00Z"),
"tutorialMark": 0,
},
{
"tutorialCode": "03",
"tutorialTitle": "Family Signs",
"tutorialDescription": "Teach signs for family members, such as mother, father, sister, brother, etc.\nIntroduce signs for common family-related words, such as family, love, and home.\nProvide visual demonstrations and practice exercises for learners to practice these family signs.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1_b3-0HAAWu5Ze20IAAKWxUSUS-fac6Dg",
"taskItems": [],
"_id": "3",
"status": 1,
"createdBy": "Nuwan Gamage",
"createdAt": new Date("2023-08-30T12:00:00Z"),
"tutorialMark": 0,
},
{
"tutorialCode": "04",
"tutorialTitle": "Basic Conversational Phrases",
"tutorialDescription": "Teach simple conversational phrases, such as \"What is your name?\" or \"How are you?\"\nIntroduce signs for common question words and phrases.\nProvide visual demonstrations and practice exercises for learners to practice these conversational phrases.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1McSxkqPb7ZnlsDKfZfTj6OTS5GvXXKFE",
"taskItems": [],
"_id": "4",
"status": 1,
"createdBy": "Nuwan Gamage",
"createdAt": new Date("2023-08-30T12:00:00Z"),
"tutorialMark": 0,
}
]
},
{
"curriculumCode": "02",
"curriculumLevel": 2,
"curriculumTitle": "Intermediate Sign Language Skills",
"curriculumDescription": "This curriculum focuses on building intermediate sign language skills, allowing learners to engage in more complex conversations and interactions using sign language.",
"curriculumImage": "https://drive.google.com/uc?export=view&id=1b5C6VNO55k3Wj6DJ-vJiUFFEuQnXs5Jo",
"tutorials": [],
"_id": "2",
"status": 1,
"createdBy": "Nuwan Gamage",
"createdAt": new Date("2023-08-30T12:00:00Z"),
"curriculumMark": 0,
},
{
"curriculumCode": "03",
"curriculumLevel": 3,
"curriculumTitle": "Advance Sign Language Skills",
"curriculumDescription": "This curriculum is designed for those who already have a solid foundation in sign language. It covers advanced topics, nuances, and cultural aspects of sign language communication.",
"curriculumImage": "https://drive.google.com/uc?export=view&id=1k-CtiCkM3efhmTBet-JZt2izxVrAdBL3",
"tutorials": [],
"_id": "3",
"status": 1,
"createdBy": "Nuwan Gamage",
"createdAt": new Date("2023-08-30T12:00:00Z"),
"curriculumMark": 100,
}
]
export interface StatusType {
id: number
code: string
description: string
}
// ==============================|| DATA - Status ||============================== //
const status: readonly StatusType[] = [
{ id: 1, code: "Active", description: "Active" },
{ id: 2, code: "New", description: "New" },
{ id: 3, code: "Pending", description: "Pending" },
{ id: 4, code: "Hold", description: "Hold" },
{ id: 5, code: "Rejected", description: "Rejected" },
];
export default status;
import { taskItemType } from "types/taskItem";
export const taskItems: taskItemType[] = [
{
"_id": "1",
"title": "Learn Number One",
"description": "In this lesson, you will learn how to sign the number one. Understanding this basic sign is crucial for counting and expressing singular items or concepts in sign language. Practice the hand shape, movement, and facial expression associated with the sign to improve your fluency.",
"howToDo": [
"- Extend your index finger straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=17sHGfW9zip8xAwbRtUihzxkseKq-Qn7q",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "2",
"title": "Learn Number Two",
"description": "In this lesson, you will learn how to sign the number two. Mastering this sign is essential for counting and expressing pairs or dual concepts in sign language. Pay attention to the hand shape, movement, and facial expression involved in signing the number two to enhance your signing skills.",
"howToDo": [
"- Extend your index and middle fingers straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."],
"referenceImage": "https://drive.google.com/uc?export=view&id=1Nm-we15Rrr04ovRC1sr-ZVMNHbnALRm2",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "3",
"title": "Learn Number Three",
"description": "In this lesson, you will learn how to sign the number three in sign language. Mastering this sign is essential for expressing the quantity or position of three items. Pay attention to the hand shape, movement, and facial expression involved in signing the number three to enhance your signing skills.",
"howToDo": [
"- Extend your index, middle, and ring fingers straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1mKY8D1utbqm8flnNM7DZRKtuPQDXqFSm",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "4",
"title": "Learn Number Four",
"description": "In this lesson, you will learn how to sign the number four in sign language. This sign is commonly used for counting or indicating the quantity or position of four items. Focus on the hand shape, movement, and expression to accurately convey the number four in sign language.",
"howToDo": [
"- Extend your thumb, index, middle, and ring fingers straight up.",
"- Keep your pinky finger folded.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1VtbHehsxbOC04fVR6_Ca6HDv6csH17SJ",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "5",
"title": "Learn Number Five",
"description": "In this lesson, you will learn how to sign the number five in sign language. Mastering this sign is crucial for counting, expressing quantities, or representing the concept of five. Practice the hand shape, movement, and facial expression to effectively communicate the number five in sign language.",
"howToDo": [
"- Extend all your fingers straight up.",
"- Keep your thumb resting on the side of your palm.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1G6qx4dhHTKUPvNag0R3qlDJugNOgcTqM",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "6",
"title": "Learn Number Six",
"description": "In this lesson, you will learn how to sign the number six in sign language. This sign is commonly used for counting, indicating quantities, or representing the concept of six. Pay attention to the hand shape, movement, and expression involved in signing the number six to enhance your signing proficiency.",
"howToDo": [
"- Extend your thumb and pinky finger straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1Q2TbcPTx8KuLp4v2mPL_7GHO0l52DZXw",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "7",
"title": "Learn Number Seven",
"description": "In this lesson, you will learn how to sign the number seven in sign language. Mastering this sign is essential for counting, expressing quantities, or representing the concept of seven. Focus on the hand shape, movement, and facial expression to accurately convey the number seven in sign language.",
"howToDo": [
"- Extend your index, middle, and ring fingers straight up.",
"- Keep your thumb, pinky, and pinky finger folded.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1Jwt1TqXO1L7t3JFqQYzKL4S4GCuqULJ1",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "8",
"title": "Learn Number Eight",
"description": "In this lesson, you will learn how to sign the number eight in sign language. This sign is commonly used for counting, indicating quantities, or representing the concept of eight. Practice the hand shape, movement, and expression to effectively communicate the number eight in sign language.",
"howToDo": [
"- Extend all your fingers straight up.",
"- Cross your index and middle fingers over your ring and pinky fingers.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1zSf4hmqIUCcfebgoD6DTWmJ3NxY36LJL",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "9",
"title": "Learn Number Nine",
"description": "In this lesson, you will learn how to sign the number nine in sign language. Mastering this sign is crucial for counting, expressing quantities, or representing the concept of nine. Pay attention to the hand shape, movement, and expression involved in signing the number nine to enhance your signing proficiency.",
"howToDo": [
"- Extend your thumb and all your fingers straight up.",
"- Keep your pinky finger folded.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=16WB1Ifhq_ozKOO-ehfIqVRB6oC3B5STw",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "10",
"title": "Learn Number Ten",
"description": "In this lesson, you will learn how to sign the number ten in sign language. This sign is commonly used for counting, indicating quantities, or representing the concept of ten. Focus on the hand shape, movement, and facial expression to accurately convey the number ten in sign language.",
"howToDo": [
"- Extend your thumb, index, and middle fingers straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=11tCYFYjdVrr5LIFGyzOrIUg8bTURATZh",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
}
]
\ No newline at end of file
import { tutorialType } from "types/tutorial";
export const tutorials: tutorialType[] = [
{
"tutorialCode": "01",
"tutorialTitle": "Numbers and Counting in Sign Language",
"tutorialDescription": "In this tutorial, you'll discover how to express numbers visually using simple hand gestures. Each number has a unique sign that involves specific finger placements and hand movements. We'll break down each number step by step, providing you with clear instructions, images, and videos to help you learn effectively.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1GeFzoy3xt8UnfCQE3IPVjPXoAg7GAWgf",
"_id": "1",
"status": 1,
"createdBy": "Nuwan Gamage",
"createdAt": new Date("2023-08-30T12:00:00Z"),
"tutorialMark": 100,
"taskItems": [
{
"_id": "1",
"title": "Learn Number One",
"description": "In this lesson, you will learn how to sign the number one. Understanding this basic sign is crucial for counting and expressing singular items or concepts in sign language. Practice the hand shape, movement, and facial expression associated with the sign to improve your fluency.",
"howToDo": [
"- Extend your index finger straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=17sHGfW9zip8xAwbRtUihzxkseKq-Qn7q",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "2",
"title": "Learn Number Two",
"description": "In this lesson, you will learn how to sign the number two. Mastering this sign is essential for counting and expressing pairs or dual concepts in sign language. Pay attention to the hand shape, movement, and facial expression involved in signing the number two to enhance your signing skills.",
"howToDo": [
"- Extend your index and middle fingers straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."],
"referenceImage": "https://drive.google.com/uc?export=view&id=1Nm-we15Rrr04ovRC1sr-ZVMNHbnALRm2",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "3",
"title": "Learn Number Three",
"description": "In this lesson, you will learn how to sign the number three in sign language. Mastering this sign is essential for expressing the quantity or position of three items. Pay attention to the hand shape, movement, and facial expression involved in signing the number three to enhance your signing skills.",
"howToDo": [
"- Extend your index, middle, and ring fingers straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1mKY8D1utbqm8flnNM7DZRKtuPQDXqFSm",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "4",
"title": "Learn Number Four",
"description": "In this lesson, you will learn how to sign the number four in sign language. This sign is commonly used for counting or indicating the quantity or position of four items. Focus on the hand shape, movement, and expression to accurately convey the number four in sign language.",
"howToDo": [
"- Extend your thumb, index, middle, and ring fingers straight up.",
"- Keep your pinky finger folded.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1VtbHehsxbOC04fVR6_Ca6HDv6csH17SJ",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "5",
"title": "Learn Number Five",
"description": "In this lesson, you will learn how to sign the number five in sign language. Mastering this sign is crucial for counting, expressing quantities, or representing the concept of five. Practice the hand shape, movement, and facial expression to effectively communicate the number five in sign language.",
"howToDo": [
"- Extend all your fingers straight up.",
"- Keep your thumb resting on the side of your palm.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1G6qx4dhHTKUPvNag0R3qlDJugNOgcTqM",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "6",
"title": "Learn Number Six",
"description": "In this lesson, you will learn how to sign the number six in sign language. This sign is commonly used for counting, indicating quantities, or representing the concept of six. Pay attention to the hand shape, movement, and expression involved in signing the number six to enhance your signing proficiency.",
"howToDo": [
"- Extend your thumb and pinky finger straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1Q2TbcPTx8KuLp4v2mPL_7GHO0l52DZXw",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "7",
"title": "Learn Number Seven",
"description": "In this lesson, you will learn how to sign the number seven in sign language. Mastering this sign is essential for counting, expressing quantities, or representing the concept of seven. Focus on the hand shape, movement, and facial expression to accurately convey the number seven in sign language.",
"howToDo": [
"- Extend your index, middle, and ring fingers straight up.",
"- Keep your thumb, pinky, and pinky finger folded.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1Jwt1TqXO1L7t3JFqQYzKL4S4GCuqULJ1",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "8",
"title": "Learn Number Eight",
"description": "In this lesson, you will learn how to sign the number eight in sign language. This sign is commonly used for counting, indicating quantities, or representing the concept of eight. Practice the hand shape, movement, and expression to effectively communicate the number eight in sign language.",
"howToDo": [
"- Extend all your fingers straight up.",
"- Cross your index and middle fingers over your ring and pinky fingers.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1zSf4hmqIUCcfebgoD6DTWmJ3NxY36LJL",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "9",
"title": "Learn Number Nine",
"description": "In this lesson, you will learn how to sign the number nine in sign language. Mastering this sign is crucial for counting, expressing quantities, or representing the concept of nine. Pay attention to the hand shape, movement, and expression involved in signing the number nine to enhance your signing proficiency.",
"howToDo": [
"- Extend your thumb and all your fingers straight up.",
"- Keep your pinky finger folded.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=16WB1Ifhq_ozKOO-ehfIqVRB6oC3B5STw",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "10",
"title": "Learn Number Ten",
"description": "In this lesson, you will learn how to sign the number ten in sign language. This sign is commonly used for counting, indicating quantities, or representing the concept of ten. Focus on the hand shape, movement, and facial expression to accurately convey the number ten in sign language.",
"howToDo": [
"- Extend your thumb, index, and middle fingers straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=11tCYFYjdVrr5LIFGyzOrIUg8bTURATZh",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
}
]
},
{
"tutorialCode": "02",
"tutorialTitle": "Everyday Vocabulary in Sign Language",
"tutorialDescription": "Teach signs for everyday objects and activities, such as eat, drink, sleep, book, pen, etc.\nIntroduce signs for common words used in daily life.\nProvide visual demonstrations and interactive exercises for learners to practice using these signs.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1QqmeBBiAojz7jaHUUdQGLyqUVR-mKSsy",
"taskItems": [],
"_id": "2",
"status": 1,
"createdBy": "Nuwan Gamage",
"createdAt": new Date("2023-08-30T12:00:00Z"),
"tutorialMark": 0,
},
{
"tutorialCode": "03",
"tutorialTitle": "Family Signs in Sign Language",
"tutorialDescription": "Teach signs for family members, such as mother, father, sister, brother, etc.\nIntroduce signs for common family-related words, such as family, love, and home.\nProvide visual demonstrations and practice exercises for learners to practice these family signs.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1_b3-0HAAWu5Ze20IAAKWxUSUS-fac6Dg",
"taskItems": [],
"_id": "3",
"status": 1,
"createdBy": "Nuwan Gamage",
"createdAt": new Date("2023-08-30T12:00:00Z"),
"tutorialMark": 0,
},
{
"tutorialCode": "04",
"tutorialTitle": "Basic Conversational Phrases in Sign Language",
"tutorialDescription": "Teach simple conversational phrases, such as \"What is your name?\" or \"How are you?\"\nIntroduce signs for common question words and phrases.\nProvide visual demonstrations and practice exercises for learners to practice these conversational phrases.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1McSxkqPb7ZnlsDKfZfTj6OTS5GvXXKFE",
"taskItems": [],
"_id": "4",
"status": 1,
"createdBy": "Nuwan Gamage",
"createdAt": new Date("2023-08-30T12:00:00Z"),
"tutorialMark": 0,
}
]
\ No newline at end of file
import { userProgressType } from "types/userProgress";
export const userProgress: userProgressType = {
"_id": "64f1f99f9a5bff5bfdb1bbc9",
"userId": "64f087fda8db35e2f70753ab",
"curriculums": [
{
"curriculumCode": "01",
"curriculumLevel": 1,
"curriculumTitle": "Basic Sign Language Skills",
"curriculumDescription": "This curriculum teaches basic sign language skills to help beginners communicate effectively using sign language.",
"curriculumImage": "https://drive.google.com/uc?export=view&id=1YACBlu7X-O7-DKv5DoW3AM9kgfT7Yhdc",
"curriculumMark": 100,
"curriculumMarkUser": 24,
"curriculumSpentTime": 15,
"tutorials": [
{
"tutorialCode": "01",
"tutorialTitle": "Numbers and Counting",
"tutorialDescription": "In this tutorial, you'll discover how to express numbers visually using simple hand gestures. Each number has a unique sign that involves specific finger placements and hand movements. We'll break down each number step by step, providing you with clear instructions, images, and videos to help you learn effectively.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1GeFzoy3xt8UnfCQE3IPVjPXoAg7GAWgf",
"tutorialMark": 100,
"tutorialMarkUser": 24,
"tutorialSpentTime": 15,
"taskItems": [
{
"title": "Learn Number One",
"description": "In this lesson, you will learn how to sign the number one. Understanding this basic sign is crucial for counting and expressing singular items or concepts in sign language. Practice the hand shape, movement, and facial expression associated with the sign to improve your fluency.",
"howToDo": [
"- Extend your index finger straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=17sHGfW9zip8xAwbRtUihzxkseKq-Qn7q",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10,
"taskItemMarkUser": 8,
"taskItemSpentTime": 5
},
{
"title": "Learn Number Two",
"description": "In this lesson, you will learn how to sign the number two. Mastering this sign is essential for counting and expressing pairs or dual concepts in sign language. Pay attention to the hand shape, movement, and facial expression involved in signing the number two to enhance your signing skills.",
"howToDo": [
"- Extend your index and middle fingers straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1Nm-we15Rrr04ovRC1sr-ZVMNHbnALRm2",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10,
"taskItemMarkUser": 8,
"taskItemSpentTime": 5
},
{
"title": "Learn Number Three",
"description": "In this lesson, you will learn how to sign the number three in sign language. Mastering this sign is essential for expressing the quantity or position of three items. Pay attention to the hand shape, movement, and facial expression involved in signing the number three to enhance your signing skills.",
"howToDo": [
"- Extend your index, middle, and ring fingers straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1mKY8D1utbqm8flnNM7DZRKtuPQDXqFSm",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10,
"taskItemMarkUser": 8,
"taskItemSpentTime": 5
},
{
"title": "Learn Number Four",
"description": "In this lesson, you will learn how to sign the number four in sign language. This sign is commonly used for counting or indicating the quantity or position of four items. Focus on the hand shape, movement, and expression to accurately convey the number four in sign language.",
"howToDo": [
"- Extend your thumb, index, middle, and ring fingers straight up.",
"- Keep your pinky finger folded.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1VtbHehsxbOC04fVR6_Ca6HDv6csH17SJ",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10,
"taskItemMarkUser": 0,
"taskItemSpentTime": 0
},
{
"title": "Learn Number Five",
"description": "In this lesson, you will learn how to sign the number five in sign language. Mastering this sign is crucial for counting, expressing quantities, or representing the concept of five. Practice the hand shape, movement, and facial expression to effectively communicate the number five in sign language.",
"howToDo": [
"- Extend all your fingers straight up.",
"- Keep your thumb resting on the side of your palm.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1G6qx4dhHTKUPvNag0R3qlDJugNOgcTqM",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10,
"taskItemMarkUser": 0,
"taskItemSpentTime": 0
},
{
"title": "Learn Number Six",
"description": "In this lesson, you will learn how to sign the number six in sign language. This sign is commonly used for counting, indicating quantities, or representing the concept of six. Pay attention to the hand shape, movement, and expression involved in signing the number six to enhance your signing proficiency.",
"howToDo": [
"- Extend your thumb and pinky finger straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1Q2TbcPTx8KuLp4v2mPL_7GHO0l52DZXw",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10,
"taskItemMarkUser": 0,
"taskItemSpentTime": 0
},
{
"title": "Learn Number Seven",
"description": "In this lesson, you will learn how to sign the number seven in sign language. Mastering this sign is essential for counting, expressing quantities, or representing the concept of seven. Focus on the hand shape, movement, and facial expression to accurately convey the number seven in sign language.",
"howToDo": [
"- Extend your index, middle, and ring fingers straight up.",
"- Keep your thumb, pinky, and pinky finger folded.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1Jwt1TqXO1L7t3JFqQYzKL4S4GCuqULJ1",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10,
"taskItemMarkUser": 0,
"taskItemSpentTime": 0
},
{
"title": "Learn Number Eight",
"description": "In this lesson, you will learn how to sign the number eight in sign language. This sign is commonly used for counting, indicating quantities, or representing the concept of eight. Practice the hand shape, movement, and expression to effectively communicate the number eight in sign language.",
"howToDo": [
"- Extend all your fingers straight up.",
"- Cross your index and middle fingers over your ring and pinky fingers.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1zSf4hmqIUCcfebgoD6DTWmJ3NxY36LJL",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10,
"taskItemMarkUser": 0,
"taskItemSpentTime": 0
},
{
"title": "Learn Number Nine",
"description": "In this lesson, you will learn how to sign the number nine in sign language. Mastering this sign is crucial for counting, expressing quantities, or representing the concept of nine. Pay attention to the hand shape, movement, and expression involved in signing the number nine to enhance your signing proficiency.",
"howToDo": [
"- Extend your thumb and all your fingers straight up.",
"- Keep your pinky finger folded.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=16WB1Ifhq_ozKOO-ehfIqVRB6oC3B5STw",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10,
"taskItemMarkUser": 0,
"taskItemSpentTime": 0
},
{
"title": "Learn Number Ten",
"description": "In this lesson, you will learn how to sign the number ten in sign language. This sign is commonly used for counting, indicating quantities, or representing the concept of ten. Focus on the hand shape, movement, and facial expression to accurately convey the number ten in sign language.",
"howToDo": [
"- Extend your thumb, index, and middle fingers straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=11tCYFYjdVrr5LIFGyzOrIUg8bTURATZh",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10,
"taskItemMarkUser": 0,
"taskItemSpentTime": 0
}
]
},
{
"tutorialCode": "02",
"tutorialTitle": "Everyday Vocabulary",
"tutorialDescription": "Teach signs for everyday objects and activities, such as eat, drink, sleep, book, pen, etc.\nIntroduce signs for common words used in daily life.\nProvide visual demonstrations and interactive exercises for learners to practice using these signs.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1QqmeBBiAojz7jaHUUdQGLyqUVR-mKSsy",
"tutorialMark": 0,
"tutorialMarkUser": 0,
"tutorialSpentTime": 0,
"taskItems": []
},
{
"tutorialCode": "03",
"tutorialTitle": "Family Signs",
"tutorialDescription": "Teach signs for family members, such as mother, father, sister, brother, etc.\nIntroduce signs for common family-related words, such as family, love, and home.\nProvide visual demonstrations and practice exercises for learners to practice these family signs.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1_b3-0HAAWu5Ze20IAAKWxUSUS-fac6Dg",
"tutorialMark": 0,
"tutorialMarkUser": 0,
"tutorialSpentTime": 0,
"taskItems": []
},
{
"tutorialCode": "04",
"tutorialTitle": "Basic Conversational Phrases",
"tutorialDescription": "Teach simple conversational phrases, such as \"What is your name?\" or \"How are you?\"\nIntroduce signs for common question words and phrases.\nProvide visual demonstrations and practice exercises for learners to practice these conversational phrases.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1McSxkqPb7ZnlsDKfZfTj6OTS5GvXXKFE",
"tutorialMark": 0,
"tutorialMarkUser": 0,
"tutorialSpentTime": 0,
"taskItems": []
}
]
},
],
"totalCurriculumsMarks": 24,
"totalCurriculumSpentTime": 15,
"status": 1,
"createdAt": new Date("2023-08-30T12:00:00Z"),
"updatedAt": new Date("2023-08-30T12:00:00Z")
}
\ No newline at end of file
// import { useMemo } from 'react';
import { useMemo } from 'react';
// material-ui
import { Theme } from '@mui/material/styles';
import { Box, useMediaQuery } from '@mui/material';
import { Theme } from '@mui/material/styles';
// project import
import Search from './Search';
import Localization from './Localization';
import Message from './Message';
import Profile from './Profile';
// import Localization from './Localization';
import Notification from './Notification';
// import Customization from './Customization';
import Profile from './Profile';
import Search from './Search';
import Customization from './Customization';
import MobileSection from './MobileSection';
// import MegaMenuSection from './MegaMenuSection';
......@@ -23,13 +23,12 @@ import { MenuOrientation } from 'types/config';
// ==============================|| HEADER - CONTENT ||============================== //
const HeaderContent = () => {
// const { i18n, menuOrientation } = useConfig();
const { menuOrientation } = useConfig();
const { i18n, menuOrientation } = useConfig();
const downLG = useMediaQuery((theme: Theme) => theme.breakpoints.down('lg'));
// eslint-disable-next-line react-hooks/exhaustive-deps
// const localization = useMemo(() => <Localization />, [i18n]);
const localization = useMemo(() => <Localization />, [i18n]);
// const megaMenu = useMemo(() => <MegaMenuSection />, []);
......@@ -38,12 +37,12 @@ const HeaderContent = () => {
{menuOrientation === MenuOrientation.HORIZONTAL && !downLG && <DrawerHeader open={true} />}
{!downLG && <Search />}
{/* {!downLG && megaMenu} */}
{/* {!downLG && localization} */}
{!downLG && localization}
{downLG && <Box sx={{ width: '100%', ml: 1 }} />}
<Notification />
<Message />
{/* <Customization /> */}
<Customization />
{!downLG && <Profile />}
{downLG && <MobileSection />}
</>
......
......@@ -3,18 +3,20 @@ import { FormattedMessage } from 'react-intl';
// assets
import {
AudioOutlined,
BookOutlined,
CarryOutOutlined,
CreditCardOutlined,
FastForwardOutlined,
FileTextOutlined,
LoginOutlined,
RedditOutlined,
RocketOutlined,
ShoppingOutlined,
TeamOutlined,
TranslationOutlined,
UserOutlined,
FastForwardOutlined,
RedditOutlined
VideoCameraOutlined,
} from '@ant-design/icons';
// type
......@@ -33,7 +35,9 @@ const icons = {
BookOutlined,
TranslationOutlined,
FastForwardOutlined,
RedditOutlined
RedditOutlined,
AudioOutlined,
VideoCameraOutlined
};
// ==============================|| MENU ITEMS - SUPPORT ||============================== //
......@@ -92,6 +96,32 @@ const application: NavItemType = {
}
]
},
{
id: 'emotion-detection',
title: <FormattedMessage id="emotion-detection" />,
type: 'collapse',
icon: icons.RedditOutlined,
children: [
{
id: 'audio-detection',
title: <FormattedMessage id="audio-detection" />,
type: 'item',
icon: icons.AudioOutlined,
url: '/emotion-detection/audio-detection',
},
{
id: 'video-detection',
title: <FormattedMessage id="video-detection" />,
type: 'item',
icon: icons.VideoCameraOutlined,
url: '/emotion-detection/video-detection',
},
]
},
{
id: 'learning-management',
title: <FormattedMessage id="learning-management" />,
......@@ -123,6 +153,12 @@ const application: NavItemType = {
type: 'item',
url: '/learning-management/curriculums-subscribed',
},
{
id: 'learning-curriculums-subscribed-tutorial',
title: <FormattedMessage id="learning-curriculums-subscribed-tutorial" />,
type: 'item',
url: '/learning-management/curriculums-subscribed-tutorial',
},
{
id: 'learning-lead-board',
title: <FormattedMessage id="learning-lead-board" />,
......
import React, { useState } from 'react';
import MainCard from 'components/MainCard';
import ScrollX from 'components/ScrollX';
import Grid from '@mui/material/Grid';
import Button from '@mui/material/Button';
import UploadOutlined from '@ant-design/icons/lib/icons/UploadOutlined';
import AudioOutlined from '@ant-design/icons/lib/icons/AudioOutlined';
const List = () => {
const [audioBlob, setAudioBlob] = useState<Blob | undefined>(undefined);
const [mediaRecorder, setMediaRecorder] = useState<MediaRecorder | undefined>(undefined);
const [isRecording, setIsRecording] = useState<boolean>(false);
const [audioUrl, setAudioUrl] = useState<string | undefined>(undefined);
const handleRecordStart = async () => {
// Clear the uploaded audio state when recording starts
setAudioUrl(undefined);
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const recorder = new MediaRecorder(stream);
recorder.ondataavailable = (event) => {
if (event.data.size > 0) {
setAudioBlob(new Blob([event.data], { type: 'audio/wav' }));
}
};
recorder.onstop = () => {
stream.getTracks().forEach(track => track.stop());
};
setMediaRecorder(recorder);
recorder.start();
setIsRecording(true);
} catch (error) {
console.error('Error starting recording:', error);
}
};
const handleRecordStop = () => {
if (mediaRecorder && isRecording) {
mediaRecorder.stop();
setIsRecording(false);
}
};
const handleUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
const files = event.target.files;
if (files && files[0] && files[0].type.startsWith('audio/')) {
const audioObjectUrl = URL.createObjectURL(files[0]);
setAudioUrl(audioObjectUrl);
} else {
// Handle case where uploaded file is not an audio file
}
};
return (
<MainCard content={false}>
<ScrollX>
<Grid container spacing={2}>
<Grid item xs={12} md={6}>
<h2>Upload or Record</h2>
<MainCard>
<div style={{ textAlign: 'center' }}>
<input
type="file"
accept="audio/*"
onChange={handleUpload}
style={{ display: 'none' }}
id="audio-upload"
/>
<label htmlFor="audio-upload">
<Button
variant="contained"
color="primary"
component="span"
startIcon={<UploadOutlined />}
>
Upload
</Button>
</label>
<Button
variant="contained"
color="primary"
startIcon={<AudioOutlined />}
onClick={isRecording ? handleRecordStop : handleRecordStart}
>
{isRecording ? 'Stop Recording' : 'Record'}
</Button>
{audioBlob && (
<audio controls>
<source src={URL.createObjectURL(audioBlob)} type="audio/wav" />
Your browser does not support the audio element.
</audio>
)}
{audioUrl && (
<audio controls>
<source src={audioUrl} type="audio/mpeg" />
Your browser does not support the audio element.
</audio>
)}
</div>
</MainCard>
</Grid>
<Grid item xs={12} md={6}>
<h2>3D Avatar</h2>
<MainCard>
{/* Content of the second card */}
{/* You can put your 3D avatar components here */}
</MainCard>
</Grid>
</Grid>
</ScrollX>
</MainCard>
);
};
export default List;
import React, { useState, useRef } from 'react';
import MainCard from 'components/MainCard';
import ScrollX from 'components/ScrollX';
import Grid from '@mui/material/Grid';
import Button from '@mui/material/Button';
import VideoCameraOutlined from '@ant-design/icons/lib/icons/VideoCameraOutlined';
import UploadOutlined from '@ant-design/icons/lib/icons/UploadOutlined';
// import WebcamOutlinedIcon from '@mui/icons-material/WebcamOutlined';
const List = () => {
const [selectedFile, setSelectedFile] = useState<File | null>(null);
const [isLive, setIsLive] = useState(false);
const fileInputRef = useRef<HTMLInputElement | null>(null);
const videoRef = useRef<HTMLVideoElement | null>(null);
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.files && event.target.files[0]) {
setSelectedFile(event.target.files[0]);
}
};
const handleUploadButtonClick = () => {
if (fileInputRef.current) {
fileInputRef.current.click();
}
};
const handleLiveButtonClick = async () => {
try {
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
if (videoRef.current) {
videoRef.current.srcObject = stream;
videoRef.current.play(); // Start playing the live camera feed
}
setIsLive(true);
} catch (error) {
console.error('Error accessing camera:', error);
}
};
return (
<MainCard content={false}>
<ScrollX>
<Grid container spacing={2}>
<Grid item xs={12} md={6}>
<h2>Upload or Live</h2>
<MainCard>
<div style={{ textAlign: 'center' }}>
<input
type="file"
accept="video/*"
ref={fileInputRef}
onChange={handleFileChange}
style={{ display: 'none' }}
/>
{isLive ? (
<video
ref={videoRef}
autoPlay
playsInline
width="100%"
/>
) : (
selectedFile && (
<video
controls
width="100%"
src={URL.createObjectURL(selectedFile)}
/>
)
)}
<Button
variant="contained"
color="primary"
startIcon={<UploadOutlined />}
onClick={handleUploadButtonClick}
>
Upload
</Button>
<Button
variant="contained"
color="primary"
startIcon={<VideoCameraOutlined />}
onClick={handleLiveButtonClick}
>
Live
</Button>
</div>
</MainCard>
</Grid>
<Grid item xs={12} md={6}>
<h2>3D Avatar</h2>
<MainCard>
{/* Content of the second card */}
{/* You can put your 3D avatar components here */}
</MainCard>
</Grid>
</Grid>
</ScrollX>
</MainCard>
);
};
export default List;
import { useEffect, useState } from 'react';
// material-ui
// third-party
import {
Accordion, AccordionDetails, AccordionSummary,
Box,
FormControl,
Grid,
MenuItem,
Pagination,
Select,
SelectChangeEvent,
Slide,
Stack,
Theme,
Typography,
useMediaQuery
} from '@mui/material';
import { useTheme } from '@mui/material/styles';
// project import
import MainCard from 'components/MainCard';
import ScrollX from 'components/ScrollX';
import usePagination from 'hooks/usePagination';
import { GlobalFilter } from 'utils/react-table';
// types
// assets
//types
import { BookOutlined } from '@ant-design/icons';
import { userProgress } from 'data/userProgress';
import CurriculumSection from 'sections/learning-management/learning-curriculums-subscribed/CurriculumSection';
import EmptyCurriculumCard from 'sections/learning-management/learning-curriculums-subscribed/skeleton/EmptyCurriculumCard';
import { curriculumTypeUserProgress, userProgressType } from 'types/userProgress';
// ==============================|| List ||============================== //
const allColumns = [
{
id: 1,
header: 'Default'
},
{
id: 2,
header: 'Title'
}
];
const List = () => {
const theme = useTheme();
const [data, setData] = useState<userProgressType["curriculums"]>([])
const matchDownSM = useMediaQuery((theme: Theme) => theme.breakpoints.down('sm'));
const [curriculumsData, setCurriculumsData] = useState<userProgressType["curriculums"]>([]);
const [sortBy, setSortBy] = useState('Default');
const [globalFilter, setGlobalFilter] = useState('');
const [page, setPage] = useState(1);
const handleChange = (event: SelectChangeEvent) => {
setSortBy(event.target.value as string);
};
// search
useEffect(() => {
const newData = data?.filter((value: any) => {
if (globalFilter) {
return value.curriculumTitle.toLowerCase().includes(globalFilter.toLowerCase());
} else {
return value;
}
});
setCurriculumsData(newData);
}, [globalFilter, data]);
const PER_PAGE = 6;
const count = Math.ceil(curriculumsData?.length! / PER_PAGE);
const _DATA = usePagination(curriculumsData, PER_PAGE);
const handleChangePage = (e: any, p: any) => {
setPage(p);
_DATA.jump(p);
};
useEffect(() => {
setData(userProgress.curriculums)
}, [])
const [expanded, setExpanded] = useState<string | false>('panel0');
const handleAccordionChange = (panel: string) => (event: React.SyntheticEvent, newExpanded: boolean) => {
setExpanded(newExpanded ? panel : false);
};
return (
<MainCard content={false}>
<ScrollX>
</ScrollX>
</MainCard>
<>
<Box sx={{ position: 'relative', marginBottom: 3 }}>
<Stack direction="row" alignItems="center">
<Stack
direction={matchDownSM ? 'column' : 'row'}
sx={{ width: '100%' }}
spacing={1}
justifyContent="space-between"
alignItems="center"
>
{/* @ts-ignore */}
<GlobalFilter preGlobalFilteredRows={data} globalFilter={globalFilter} setGlobalFilter={setGlobalFilter} />
<Stack direction={matchDownSM ? 'column' : 'row'} alignItems="center" spacing={1}>
<FormControl sx={{ m: 1, minWidth: 120 }}>
<Select
value={sortBy}
onChange={handleChange}
displayEmpty
inputProps={{ 'aria-label': 'Without label' }}
renderValue={(selected) => {
if (!selected) {
return <Typography variant="subtitle1">Sort By</Typography>;
}
return <Typography variant="subtitle2">Sort by ({sortBy})</Typography>;
}}
>
{allColumns.map((column) => {
return (
<MenuItem key={column.id} value={column.header}>
{column.header}
</MenuItem>
);
})}
</Select>
</FormControl>
</Stack>
</Stack>
</Stack>
</Box>
<Grid container spacing={3}>
{curriculumsData?.length! > 0 ? (
_DATA
.currentData()
.sort(function (a: any, b: any) {
if (sortBy === 'Title') return a.curriculumTitle.localeCompare(b.curriculumTitle);
return a;
})
.map((curriculum: curriculumTypeUserProgress, index: number) => (
<Slide key={index} direction="up" in={true} timeout={50}>
<Grid item xs={12} sm={12} lg={12}>
<Box
sx={{
'& .MuiAccordion-root': {
borderColor: theme.palette.divider,
'& .MuiAccordionSummary-root': {
bgcolor: 'transparent',
flexDirection: 'row',
'&:focus-visible': {
bgcolor: 'primary.lighter'
}
},
'& .MuiAccordionDetails-root': {
borderColor: theme.palette.divider
},
'& .Mui-expanded': {
color: theme.palette.primary.main
}
}
}}
>
<Accordion expanded={expanded === `panel${index}`} onChange={handleAccordionChange(`panel${index}`)}>
<AccordionSummary aria-controls={`panel${index}d-content`} id={`panel${index}d-header`}>
<Stack direction="row" spacing={1.5} alignItems="center">
<BookOutlined style={{ fontSize: '26px' }} />
<Typography variant="h4">{curriculum.curriculumTitle}</Typography>
</Stack>
</AccordionSummary>
<AccordionDetails>
<CurriculumSection curriculum={curriculum} />
</AccordionDetails>
</Accordion>
</Box>
</Grid>
</Slide>
))
) : (
<EmptyCurriculumCard title={'System have no curriculumsData yet.'} />
)}
</Grid>
<Stack spacing={2} sx={{ p: 2.5 }} alignItems="flex-end">
<Pagination
count={count}
size="medium"
page={page}
showFirstButton
showLastButton
variant="combined"
color="primary"
onChange={handleChangePage}
/>
</Stack>
</>
)
}
......
import { useEffect, useRef, useState } from "react";
// material-ui
import { Alert, Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, Divider, Grid, LinearProgress, List, ListItemAvatar, ListItemButton, ListItemSecondaryAction, ListItemText, Stack, Typography } from "@mui/material";
import { useTheme } from '@mui/material/styles';
// project import
import AntAvatar from 'components/@extended/Avatar';
import MainCard from "components/MainCard";
// types
import { tutorialTypeUserProgress } from "types/userProgress";
// assets
import { CheckCircleOutlined, CheckOutlined, ClockCircleOutlined, FileImageOutlined, FileMarkdownFilled, LeftOutlined, PlayCircleOutlined, RightOutlined, TrophyOutlined, UserOutlined, VideoCameraOutlined } from "@ant-design/icons";
import { PopupTransition } from "components/@extended/Transitions";
import ReportCard from "components/cards/statistics/ReportCard";
import { userProgress } from "data/userProgress";
import { selectedCommonDataProps, selectedItemContentProps } from "./types/types";
import Webcam from 'react-webcam';
// action style
const actionSX = {
mt: 0.75,
ml: 1,
top: 'auto',
right: 'auto',
alignSelf: 'flex-start',
transform: 'none'
};
function convertMinutesToHMS(minutes: number) {
const hours = Math.floor(minutes / 60);
const remainingMinutes = minutes % 60;
const seconds = Math.round((minutes % 1) * 60);
return `${hours} hr ${remainingMinutes} min ${seconds} sec`;
}
// ==============================|| Tutorial ||============================== //
const Tutorial = () => {
const theme = useTheme();
const [data, setData] = useState<tutorialTypeUserProgress>()
const [selectedItem, setSelectedItem] = useState<{ selectedCommonData: selectedCommonDataProps | null, backgroundColor: any | null }>({
selectedCommonData: null,
backgroundColor: 'white', // Initial background color
});
const [selectedItemContent, setSelectedItemContent] = useState<selectedItemContentProps | null>(null)
const [desc, setDesc] = useState("")
const [readMore, setReadMore] = useState(false)
const [itemDesc, setItemDesc] = useState("")
const [itemReadMore, setItemReadMore] = useState(false)
const [itemReferenceData, setItemReferenceData] = useState<"image" | "video">("image")
const handleItemClick = (item: selectedCommonDataProps, backgroundColor: any) => {
setSelectedItem({
selectedCommonData: {
userId: item.userId,
curriculumCode: item.curriculumCode,
tutorialCode: item.tutorialCode,
title: item.title
},
backgroundColor,
});
};
useEffect(() => {
// Set your data here
setData(userProgress.curriculums![0].tutorials[0]);
}, []);
useEffect(() => {
setDesc(data?.tutorialDescription?.slice(0, 200)!)
}, [data])
useEffect(() => {
if (!selectedItemContent?.description) return
setItemDesc(selectedItemContent?.description.slice(0, 200)!)
}, [selectedItemContent])
useEffect(() => {
// Filter data based on selectedItem.title
if (selectedItem && data) {
const filteredItem = data.taskItems.find((item) => item.title === selectedItem.selectedCommonData?.title);
setSelectedItemContent({
userId: selectedItem.selectedCommonData?.userId!,
curriculumCode: selectedItem.selectedCommonData?.curriculumCode!,
tutorialCode: selectedItem.selectedCommonData?.tutorialCode!,
...filteredItem!
} || null)
} else {
setSelectedItemContent(null);
}
}, [selectedItem]);
//alert model
const [openModel, setOpenModel] = useState(false);
const handleModelClose = () => {
setOpenModel(!openModel);
};
// web cam
const webcamRef = useRef<Webcam>(null);
const [capturedImage, setCapturedImage] = useState<string | null>(null);
const capture = () => {
if (webcamRef.current) {
const imageSrc = webcamRef.current.getScreenshot();
setCapturedImage(imageSrc);
}
};
return (
<>
<Grid container spacing={2}>
<Grid item md={12}>
<MainCard title="Overview">
<Grid container spacing={2}>
<Grid
item
xs={7}
sm={7}
sx={{}}
>
<Stack>
<Typography variant="h3" sx={{ mb: 2 }}>
{data?.tutorialTitle}
</Typography>
<Typography variant="h5" sx={{ textAlign: "justify" }}>
{desc}
<span style={{ fontWeight: "bold", cursor: "pointer" }}
onClick={() => {
if (!readMore) {
setDesc(data?.tutorialDescription!)
setReadMore(true)
} else {
setDesc(data?.tutorialDescription?.slice(0, 200)!)
setReadMore(false)
}
}} color="secondary">
{readMore ? "Show Less" : "...Read More"}
</span>
</Typography>
<Typography variant="h4" sx={{ pt: 3, pb: 1, zIndex: 1 }}>
{(data?.tutorialMarkUser! / data?.tutorialMark!) * 100}% Completed
</Typography>
<Box sx={{ maxWidth: '60%' }}>
<LinearProgress variant="determinate" color="success" value={(data?.tutorialMarkUser! / data?.tutorialMark!) * 100} />
</Box>
</Stack>
</Grid>
<Grid md={5} item>
<Grid container spacing={2}>
<Grid md={12} item>
<ReportCard
primary={`${data?.tutorialMarkUser?.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
secondary="Tutorial User Mark" color={theme.palette.error.main} iconPrimary={FileMarkdownFilled} />
</Grid>
<Grid md={12} item>
<ReportCard primary={`${convertMinutesToHMS(data?.tutorialSpentTime!)}`} secondary="Tutorial Time Spent On" color={theme.palette.success.dark} iconPrimary={ClockCircleOutlined} />
</Grid>
</Grid>
</Grid>
</Grid>
</MainCard>
</Grid>
<Grid item md={4}>
<MainCard title="Task List">
<List
component="nav"
sx={{
py: 0,
'& .MuiListItemButton-root': {
'& .MuiListItemSecondaryAction-root': { ...actionSX, position: 'relative' }
}
}}
>
{data?.taskItems.map((item, index) => {
const isSelected = selectedItem.selectedCommonData?.title === item.title;
const backgroundColor = isSelected ? theme.palette.primary.lighter : 'white';
const iconColor = isSelected ? 'warning' : 'success';
return (
<>
<ListItemButton
key={index}
divider
onClick={() => handleItemClick({
userId: "",
curriculumCode: "",
tutorialCode: data.tutorialCode!,
title: item.title
}, backgroundColor)}
sx={{ backgroundColor }}
>
<ListItemAvatar>
<AntAvatar alt="Basic" type="combined" color={iconColor}>
<ClockCircleOutlined />
</AntAvatar>
</ListItemAvatar>
<ListItemText primary={<Typography variant="subtitle1">{item.title}</Typography>} />
<ListItemSecondaryAction>
<Stack alignItems="flex-end">
<Typography variant="subtitle1" noWrap>User Mark : {item.taskItemMarkUser?.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Typography>
<Typography variant="h6" color="secondary" noWrap> Mark : {item.taskItemMark?.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})} </Typography>
</Stack>
</ListItemSecondaryAction>
</ListItemButton>
</>
)
})}
</List>
</MainCard>
</Grid>
<Grid item md={8}>
<MainCard title="Task View">
<Grid container spacing={2}>
{selectedItemContent !== null
?
<>
<Grid item md={8}>
<Typography variant="h4" sx={{ mb: 2 }}>
{selectedItemContent?.title}
</Typography>
<Typography variant="h5" sx={{ textAlign: "justify", mb: 2 }}>
{itemDesc}
<span style={{ fontWeight: "bold", cursor: "pointer" }}
onClick={() => {
if (!itemReadMore) {
setItemDesc(selectedItemContent?.description!)
setItemReadMore(true)
} else {
setItemDesc(selectedItemContent?.description?.slice(0, 200)!)
setItemReadMore(false)
}
}} color="secondary">
{itemReadMore ? "Show Less" : "...Read More"}
</span>
</Typography>
<MainCard title="How To Do">
<List
component="nav"
sx={{
py: 0,
'& .MuiListItemButton-root': {
'& .MuiListItemSecondaryAction-root': { ...actionSX, position: 'relative' }
}
}}
>
{selectedItemContent?.howToDo && selectedItemContent?.howToDo.map((i, index) => {
return (
<>
<ListItemButton divider>
<ListItemAvatar>
<AntAvatar alt="Basic" type="combined" color="info">
{index + 1}
</AntAvatar>
</ListItemAvatar>
<ListItemText primary={<Typography variant="subtitle1">{i}</Typography>} />
</ListItemButton>
</>
)
})}
</List>
</MainCard>
</Grid>
<Grid item xs={4}>
<Grid container spacing={2}>
<Grid item md={6}>
<Button
variant="outlined"
endIcon={<FileImageOutlined />}
sx={{ my: 2 }}
onClick={() => { setItemReferenceData("image") }}
color="success"
size="small"
>
Image Reference
</Button>
</Grid>
<Grid item md={6}>
<Button
variant="outlined"
endIcon={<VideoCameraOutlined />}
sx={{ my: 2 }}
onClick={() => { setItemReferenceData("video") }}
color="warning"
size="small"
>
Vide Reference
</Button>
</Grid>
<Grid item md={12} xs={4} sx={{ height: '246px', '& img': { mb: 0, width: '100%', height: '100%', objectFit: 'contain' } }}>
{itemReferenceData == "image" && <img src={selectedItemContent?.referenceImage} alt="feature" style={{ width: '100%', height: '100%' }} />}
{itemReferenceData == "video" && <>In Constriction ....</>}
</Grid>
</Grid>
</Grid>
<Grid item md={8}>
<Grid container spacing={2}>
<Grid item md={6}>
<ReportCard primary={`Task Mark : ${(selectedItemContent.taskItemMark)?.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
secondary="Task Mark" color={theme.palette.success.dark} iconPrimary={CheckCircleOutlined}
/>
</Grid>
<Grid item md={6}>
<ReportCard primary={`User Mark : ${(selectedItemContent.taskItemMarkUser)?.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
secondary="Task User Mark" color={theme.palette.success.dark} iconPrimary={UserOutlined}
/>
</Grid>
<Grid item md={12}>
<ReportCard
primary={`Pass Mark : ${(selectedItemContent.taskItemMark * (85 / 100))?.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
secondary="To Complete the task you should get 85% or higher of the task mark" color={theme.palette.success.dark} iconPrimary={TrophyOutlined}
/>
</Grid>
</Grid>
</Grid>
<Grid item md={4}>
<Grid container spacing={1}>
<Grid item md={12}>
<Button
variant="contained"
endIcon={<PlayCircleOutlined />}
fullWidth
sx={{ my: 1, width: "100%", height: "100%" }}
onClick={() => { setOpenModel(true) }}
color="warning"
size="small"
>
Practice Task
</Button>
</Grid>
<Grid item md={12}>
<Button
variant="contained"
endIcon={<CheckOutlined />}
fullWidth
sx={{ my: 1, width: "100%", height: "100%" }}
onClick={() => { }}
color="success"
size="small"
>
Complete Task
</Button>
</Grid>
<Grid item md={6}>
<Button
variant="contained"
endIcon={<LeftOutlined />}
fullWidth
sx={{ my: 1, width: "100%", height: "100%" }}
onClick={() => { }}
color="secondary"
size="small"
>
Previous Task
</Button>
</Grid>
<Grid item md={6}>
<Button
variant="contained"
endIcon={<RightOutlined />}
fullWidth
sx={{ my: 1, width: "100%", height: "100%" }}
onClick={() => { }}
color={"info"}
size="small"
>
Next Task
</Button>
</Grid>
</Grid>
</Grid>
</>
:
<Grid item md={12}>
<Alert variant="border" color="warning">Select a Task</Alert>
</Grid>
}
</Grid>
</MainCard>
</Grid>
</Grid>
<Dialog
maxWidth="lg"
TransitionComponent={PopupTransition}
keepMounted
fullWidth
onClose={handleModelClose}
open={openModel}
sx={{ '& .MuiDialog-paper': { p: 0 }, transition: 'transform 225ms' }}
aria-describedby="alert-dialog-slide-description"
>
<DialogTitle>Practice</DialogTitle>
<Divider />
<DialogContent sx={{ p: 2.5 }}>
<Grid container spacing={2}>
<Grid item md={8}>
<MainCard title="Capture / Upload Image">
<Grid container spacing={2}>
<Grid item md={6}>
<Webcam
audio={false}
ref={webcamRef}
screenshotFormat="image/jpeg"
height={300}
width={400}
// videoConstraints={videoConstraints}
/>
</Grid>
<Grid item md={6}>
{capturedImage && (
<img src={capturedImage} alt="Captured" style={{ width: 400, height: 300 }} />
)}
</Grid>
</Grid>
</MainCard>
</Grid>
<Grid item md={4}>
<MainCard title="Practice Result">
<Grid container spacing={2}>
<Grid item md={12}>
<ReportCard
primary={`Result : N/A`}
secondary="To Complete the task you should get 85% or higher of the task mark" color={theme.palette.success.dark} />
</Grid>
<Grid item md={12}>
<ReportCard
primary={`Status : N/A`}
secondary="To Complete the task you should get 85% or higher of the task mark" color={theme.palette.success.dark} />
</Grid>
<Grid item md={12}>
<Button variant="contained" color="success" onClick={() => { }} fullWidth>
Get Result
</Button>
</Grid>
</Grid>
</MainCard>
</Grid>
<Grid item md={8}>
<Button size="small" variant="contained" onClick={capture} fullWidth>
Capture
</Button>
</Grid>
<Grid item md={4} />
</Grid>
</DialogContent>
<Divider />
<DialogActions sx={{ p: 2.5 }}>
<Grid container spacing={2}>
<Grid item md={12}>
<Button type="button" variant="contained" sx={{ float: "right" }} onClick={handleModelClose}>
Close
</Button>
</Grid>
</Grid>
</DialogActions>
</Dialog>
</>
)
}
export default Tutorial;
import { taskItemTypeUserProgress } from "types/userProgress";
export interface selectedItemContentProps extends taskItemTypeUserProgress {
userId: string
curriculumCode: string
tutorialCode: string
}
export interface selectedCommonDataProps {
userId: string
curriculumCode: string
tutorialCode: string
title: string
}
\ No newline at end of file
import { useEffect, useState } from 'react';
// material-ui
// third-party
import {
Box,
FormControl,
Grid,
MenuItem,
Pagination,
Select,
SelectChangeEvent,
Slide,
Stack,
Theme,
Typography,
useMediaQuery
} from '@mui/material';
// project import
import MainCard from 'components/MainCard';
import ScrollX from 'components/ScrollX';
import usePagination from 'hooks/usePagination';
import CurriculumCard from 'sections/learning-management/learning-curriculums/CurriculumCard';
import EmptyCurriculumCard from 'sections/learning-management/learning-curriculums/skeleton/EmptyCurriculumCard';
import { curriculumType } from 'types/curriculum';
import { GlobalFilter } from 'utils/react-table';
// assets
// types
//types
// assets
import { curriculums } from 'data/curriculums';
// ==============================|| List ||============================== //
const allColumns = [
{
id: 1,
header: 'Default'
},
{
id: 2,
header: 'Title'
}
];
const List = () => {
const [data, setData] = useState<curriculumType[]>([])
const matchDownSM = useMediaQuery((theme: Theme) => theme.breakpoints.down('sm'));
const [curriculumsData, setCurriculumsData] = useState<curriculumType[]>([]);
const [sortBy, setSortBy] = useState('Default');
const [globalFilter, setGlobalFilter] = useState('');
const [page, setPage] = useState(1);
const handleChange = (event: SelectChangeEvent) => {
setSortBy(event.target.value as string);
};
// search
useEffect(() => {
const newData = data.filter((value: any) => {
if (globalFilter) {
return value.curriculumTitle.toLowerCase().includes(globalFilter.toLowerCase());
} else {
return value;
}
});
setCurriculumsData(newData);
}, [globalFilter, data]);
const PER_PAGE = 6;
const count = Math.ceil(curriculumsData.length / PER_PAGE);
const _DATA = usePagination(curriculumsData, PER_PAGE);
const handleChangePage = (e: any, p: any) => {
setPage(p);
_DATA.jump(p);
};
useEffect(() => {
setData(curriculums)
}, [])
return (
<MainCard content={false}>
<ScrollX>
</ScrollX>
</MainCard>
<>
<Box sx={{ position: 'relative', marginBottom: 3 }}>
<Stack direction="row" alignItems="center">
<Stack
direction={matchDownSM ? 'column' : 'row'}
sx={{ width: '100%' }}
spacing={1}
justifyContent="space-between"
alignItems="center"
>
{/* @ts-ignore */}
<GlobalFilter preGlobalFilteredRows={data} globalFilter={globalFilter} setGlobalFilter={setGlobalFilter} />
<Stack direction={matchDownSM ? 'column' : 'row'} alignItems="center" spacing={1}>
<FormControl sx={{ m: 1, minWidth: 120 }}>
<Select
value={sortBy}
onChange={handleChange}
displayEmpty
inputProps={{ 'aria-label': 'Without label' }}
renderValue={(selected) => {
if (!selected) {
return <Typography variant="subtitle1">Sort By</Typography>;
}
return <Typography variant="subtitle2">Sort by ({sortBy})</Typography>;
}}
>
{allColumns.map((column) => {
return (
<MenuItem key={column.id} value={column.header}>
{column.header}
</MenuItem>
);
})}
</Select>
</FormControl>
</Stack>
</Stack>
</Stack>
</Box>
<Grid container spacing={3}>
{curriculumsData.length > 0 ? (
_DATA
.currentData()
.sort(function (a: any, b: any) {
if (sortBy === 'Title') return a.curriculumTitle.localeCompare(b.curriculumTitle);
return a;
})
.map((curriculum: curriculumType, index: number) => (
<Slide key={index} direction="up" in={true} timeout={50}>
<Grid item xs={12} sm={6} lg={4}>
<CurriculumCard curriculum={curriculum} />
</Grid>
</Slide>
))
) : (
<EmptyCurriculumCard title={'System have no curriculumsData yet.'} />
)}
</Grid>
<Stack spacing={2} sx={{ p: 2.5 }} alignItems="flex-end">
<Pagination
count={count}
size="medium"
page={page}
showFirstButton
showLastButton
variant="combined"
color="primary"
onChange={handleChangePage}
/>
</Stack>
</>
)
}
......
export interface dataProps {
_id: number | string | undefined;
curriculumCode: string;
curriculumLevel: number;
curriculumName: string;
curriculumDescription: string;
curriculumImage: string;
tutorials?: tutorialItemProps[];
status?: number;
createdBy?: string;
updatedBy?: string;
createdAt?: Date;
updatedAt?: Date;
}
export interface tutorialItemProps {
_id: number | string | undefined;
tutorialCode: string;
tutorialTitle: string;
tutorialDescription: string;
tutorialImage: string;
status?: number;
createdBy?: string;
updatedBy?: string;
createdAt?: Date;
updatedAt?: Date;
taskItems?: taskItemProps[]
}
export interface taskItemProps {
_id: number | string | undefined;
title: string;
description: string;
howToDo: string;
referenceImage: string;
referenceVideo: string;
}
\ No newline at end of file
......@@ -3,6 +3,7 @@ import { MouseEvent, useMemo, useState } from 'react';
// material-ui
import {
Button,
Dialog,
IconButton,
Stack,
Table,
......@@ -32,7 +33,9 @@ import {
import { DeleteTwoTone, EditTwoTone, EyeTwoTone, PlusOutlined } from '@ant-design/icons';
//types
import AlertTutorialDelete from 'sections/parameters/tutorial-management/AlertTutorialDelete';
import { PopupTransition } from 'components/@extended/Transitions';
import AddEditCurriculum from 'sections/parameters/curriculum-management/AddEditCurriculum';
import AlertCurriculumDelete from 'sections/parameters/curriculum-management/AlertCurriculumDelete';
import { ReactTableProps, curriculumProps, dataProps } from './types/types';
// ==============================|| REACT TABLE ||============================== //
......@@ -154,7 +157,7 @@ const List = () => {
},
{
Header: 'Curriculum Name',
accessor: 'curriculumName'
accessor: 'curriculumTitle'
},
{
Header: 'Status',
......@@ -205,8 +208,8 @@ const List = () => {
color="error"
onClick={(e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
// setTutorialId(row.values.id)
// setOpenAlert(true)
setCurriculumId(row.values._id)
setOpenAlert(true)
}}
>
<DeleteTwoTone twoToneColor={theme.palette.error.main} />
......@@ -232,9 +235,7 @@ const List = () => {
//alert model
const [openAlert, setOpenAlert] = useState(false);
// const [curriculumId, setCurriculumId] = useState<number | null>(null)
const curriculumId : number | null = null
const [curriculumId, setCurriculumId] = useState<number | string | undefined>(undefined)
const handleAlertClose = () => {
setOpenAlert(!openAlert);
......@@ -246,8 +247,21 @@ const List = () => {
<ScrollX>
<ReactTable columns={columns} data={data} handleAddEdit={handleAddEdit} />
</ScrollX>
{/* add / edit curriculum dialog */}
<Dialog
maxWidth="sm"
TransitionComponent={PopupTransition}
keepMounted
fullWidth
onClose={handleAddEdit}
open={addEdit}
sx={{ '& .MuiDialog-paper': { p: 0 }, transition: 'transform 225ms' }}
aria-describedby="alert-dialog-slide-description"
>
<AddEditCurriculum curriculum={curriculum} onCancel={handleAddEdit} />
</Dialog>
{/* alert model */}
{!curriculum && <AlertTutorialDelete title={""} open={openAlert} handleClose={handleAlertClose} deleteId={curriculumId} />}
{!curriculum && <AlertCurriculumDelete title={""} open={openAlert} handleClose={handleAlertClose} deleteId={curriculumId} />}
</MainCard>
</>
)
......
......@@ -2,16 +2,17 @@ import { Column } from 'react-table';
export interface dataProps {
_id: number | string | undefined;
curriculumCode: String;
curriculumLevel: String;
curriculumName: String;
curriculumImage: String;
curriculumCode: string;
curriculumLevel: number;
curriculumName: string;
curriculumDescription: string;
curriculumImage: string;
tutorials: tutorialItemProps[];
status: Number;
createdBy: String;
updatedBy: String;
status: number;
createdBy: string;
updatedBy: string;
createdAt: Date;
updatedAt: Date;
updatedAt: Date;
}
export interface ReactTableProps {
......@@ -22,26 +23,28 @@ export interface ReactTableProps {
export interface curriculumProps {
_id: number | string | undefined;
curriculumCode: String;
curriculumLevel: String;
curriculumName: String;
curriculumImage: String;
tutorials: tutorialItemProps[];
status: Number;
createdBy: String;
updatedBy: String;
curriculumCode: string;
curriculumLevel: number;
curriculumTitle: string;
curriculumDescription: string;
curriculumImage: string;
tutorials: string[];
status: number;
createdBy: string;
updatedBy: string;
createdAt: Date;
updatedAt: Date;
updatedAt: Date;
}
export interface tutorialItemProps {
_id: number | string | undefined;
tutorialCode: String;
tutorialTitle: String;
tutorialImage: String;
status: Number;
createdBy: String;
updatedBy: String;
tutorialCode: string;
tutorialTitle: string;
tutorialDescription: string;
tutorialImage: string;
status: number;
createdBy: string;
updatedBy: string;
createdAt: Date;
updatedAt: Date;
taskItems: taskItemProps[]
......@@ -49,9 +52,9 @@ export interface tutorialItemProps {
export interface taskItemProps {
_id: number | string | undefined;
title: String;
description: String;
howToDo: String;
referenceImage: String;
referenceVideo: String;
title: string;
description: string;
howToDo: string;
referenceImage: string;
referenceVideo: string;
}
\ No newline at end of file
......@@ -3,6 +3,7 @@ import { MouseEvent, useMemo, useState } from 'react';
// material-ui
import {
Button,
Dialog,
IconButton,
Stack,
Table,
......@@ -18,6 +19,7 @@ import {
import { Cell, Column, HeaderGroup, Row, useFilters, useGlobalFilter, usePagination, useTable } from 'react-table';
// project import
import { PopupTransition } from 'components/@extended/Transitions';
import MainCard from 'components/MainCard';
import ScrollX from 'components/ScrollX';
import { CSVExport, EmptyTable, TablePagination } from 'components/third-party/ReactTable';
......@@ -32,6 +34,7 @@ import {
import { DeleteTwoTone, EditTwoTone, EyeTwoTone, PlusOutlined } from '@ant-design/icons';
//types
import AddEditTutorial from 'sections/parameters/tutorial-management/AddEditTutorial';
import AlertTutorialDelete from 'sections/parameters/tutorial-management/AlertTutorialDelete';
import { ReactTableProps, dataProps, tutorialProps } from './types/types';
......@@ -121,7 +124,143 @@ const List = () => {
const theme = useTheme();
// table
const data: dataProps[] = []
const data: dataProps[] = [
{
"_id": "",
"tutorialCode": "01",
"tutorialTitle": "Numbers and Counting in Sign Language",
"tutorialDescription": "In this tutorial, you'll discover how to express numbers visually using simple hand gestures. Each number has a unique sign that involves specific finger placements and hand movements. We'll break down each number step by step, providing you with clear instructions, images, and videos to help you learn effectively.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1YACBlu7X-O7-DKv5DoW3AM9kgfT7Yhdc",
"taskItems": [
{
"_id": "",
"title": "Learn Number One",
"description": "Learn how to sign the number one in sign language.",
"howToDo": "- Extend your index finger straight up.\n- Keep the rest of your fingers closed.\n- Hold your hand in front of your chest.",
"referenceImage": "https://example.com/number_one.jpg",
"referenceVideo": "https://example.com/number_one_video.mp4",
"taskItemMark": 10
},
{
"_id": "",
"title": "Learn Number Two",
"description": "Learn how to sign the number two in sign language.",
"howToDo": "- Extend your index and middle fingers straight up.\n- Keep the rest of your fingers closed.\n- Hold your hand in front of your chest.",
"referenceImage": "https://example.com/number_two.jpg",
"referenceVideo": "https://example.com/number_two_video.mp4",
"taskItemMark": 10
},
{
"_id": "",
"title": "Learn Number Three",
"description": "Learn how to sign the number three in sign language.",
"howToDo": "- Extend your index, middle, and ring fingers straight up.\n- Keep the rest of your fingers closed.\n- Hold your hand in front of your chest.",
"referenceImage": "https://example.com/number_three.jpg",
"referenceVideo": "https://example.com/number_three_video.mp4",
"taskItemMark": 10
},
{
"_id": "",
"title": "Learn Number Four",
"description": "Learn how to sign the number four in sign language.",
"howToDo": "- Extend your thumb, index, middle, and ring fingers straight up.\n- Keep your pinky finger folded.\n- Hold your hand in front of your chest.",
"referenceImage": "https://example.com/number_four.jpg",
"referenceVideo": "https://example.com/number_four_video.mp4",
"taskItemMark": 10
},
{
"_id": "",
"title": "Learn Number Five",
"description": "Learn how to sign the number five in sign language.",
"howToDo": "- Extend all your fingers straight up.\n- Keep your thumb resting on the side of your palm.\n- Hold your hand in front of your chest.",
"referenceImage": "https://example.com/number_five.jpg",
"referenceVideo": "https://example.com/number_five_video.mp4",
"taskItemMark": 10
},
{
"_id": "",
"title": "Learn Number Six",
"description": "Learn how to sign the number six in sign language.",
"howToDo": "- Extend your thumb and pinky finger straight up.\n- Keep the rest of your fingers closed.\n- Hold your hand in front of your chest.",
"referenceImage": "https://example.com/number_six.jpg",
"referenceVideo": "https://example.com/number_six_video.mp4",
"taskItemMark": 10
},
{
"_id": "",
"title": "Learn Number Seven",
"description": "Learn how to sign the number seven in sign language.",
"howToDo": "- Extend your index, middle, and ring fingers straight up.\n- Keep your thumb, pinky, and pinky finger folded.\n- Hold your hand in front of your chest.",
"referenceImage": "https://example.com/number_seven.jpg",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "",
"title": "Learn Number Eight",
"description": "Learn how to sign the number eight in sign language.",
"howToDo": "- Extend all your fingers straight up.\n- Cross your index and middle fingers over your ring and pinky fingers.\n- Hold your hand in front of your chest.",
"referenceImage": "https://example.com/number_eight.jpg",
"referenceVideo": "https://example.com/number_eight_video.mp4",
"taskItemMark": 10
},
{
"_id": "",
"title": "Learn Number Nine",
"description": "Learn how to sign the number nine in sign language.",
"howToDo": "- Extend your thumb and all your fingers straight up.\n- Keep your pinky finger folded.\n- Hold your hand in front of your chest.",
"referenceImage": "https://example.com/number_nine.jpg",
"referenceVideo": "https://example.com/number_nine_video.mp4",
"taskItemMark": 10
},
{
"_id": "",
"title": "Learn Number Ten",
"description": "Learn how to sign the number ten in sign language.",
"howToDo": "- Extend your thumb, index, and middle fingers straight up.\n- Keep the rest of your fingers closed.\n- Hold your hand in front of your chest.",
"referenceImage": "https://example.com/number_ten.jpg",
"referenceVideo": "https://example.com/number_ten_video.mp4",
"taskItemMark": 10
}
],
"status": 1,
"createdAt": new Date("2023-08-30T12:00:00Z"),
"createdBy": "Nuwan Gamage"
},
{
"_id": "",
"tutorialCode": "02",
"tutorialTitle": "Everyday Vocabulary in Sign Language",
"tutorialDescription": "Teach signs for everyday objects and activities, such as eat, drink, sleep, book, pen, etc.\nIntroduce signs for common words used in daily life.\nProvide visual demonstrations and interactive exercises for learners to practice using these signs.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1YACBlu7X-O7-DKv5DoW3AM9kgfT7Yhdc",
"taskItems": [],
"status": 1,
"createdAt": new Date("2023-08-30T12:00:00Z"),
"createdBy": "Nuwan Gamage"
},
{
"_id": "",
"tutorialCode": "03",
"tutorialTitle": "Family Signs in Sign Language",
"tutorialDescription": "Teach signs for family members, such as mother, father, sister, brother, etc.\nIntroduce signs for common family-related words, such as family, love, and home.\nProvide visual demonstrations and practice exercises for learners to practice these family signs.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1YACBlu7X-O7-DKv5DoW3AM9kgfT7Yhdc",
"taskItems": [],
"status": 1,
"createdAt": new Date("2023-08-30T12:00:00Z"),
"createdBy": "Nuwan Gamage"
},
{
"_id": "",
"tutorialCode": "04",
"tutorialTitle": "Basic Conversational Phrases in Sign Language",
"tutorialDescription": "Teach simple conversational phrases, such as \"What is your name?\" or \"How are you?\"\nIntroduce signs for common question words and phrases.\nProvide visual demonstrations and practice exercises for learners to practice these conversational phrases.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1YACBlu7X-O7-DKv5DoW3AM9kgfT7Yhdc",
"taskItems": [],
"status": 1,
"createdAt": new Date("2023-08-30T12:00:00Z"),
"createdBy": "Nuwan Gamage"
}
]
const columns = useMemo(
() =>
......@@ -149,7 +288,7 @@ const List = () => {
accessor: 'tutorialCode'
},
{
Header: 'Tutorial Title',
Header: 'Tutorial Name',
accessor: 'tutorialTitle'
},
{
......@@ -201,8 +340,8 @@ const List = () => {
color="error"
onClick={(e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
// setTutorialId(row.values.id)
// setOpenAlert(true)
setTutorialId(row.values._id)
setOpenAlert(true)
}}
>
<DeleteTwoTone twoToneColor={theme.palette.error.main} />
......@@ -228,9 +367,7 @@ const List = () => {
//alert model
const [openAlert, setOpenAlert] = useState(false);
// const [tutorialId, setTutorialId] = useState<number | null>(null)
const tutorialId: number | null = null
const [tutorialId, setTutorialId] = useState<number | string | undefined>(undefined)
const handleAlertClose = () => {
setOpenAlert(!openAlert);
......@@ -242,6 +379,19 @@ const List = () => {
<ScrollX>
<ReactTable columns={columns} data={data} handleAddEdit={handleAddEdit} />
</ScrollX>
{/* add / edit tutorial dialog */}
<Dialog
maxWidth="sm"
TransitionComponent={PopupTransition}
keepMounted
fullWidth
onClose={handleAddEdit}
open={addEdit}
sx={{ '& .MuiDialog-paper': { p: 0 }, transition: 'transform 225ms' }}
aria-describedby="alert-dialog-slide-description"
>
<AddEditTutorial tutorial={tutorial} onCancel={handleAddEdit} />
</Dialog>
{/* alert model */}
{!tutorial && <AlertTutorialDelete title={""} open={openAlert} handleClose={handleAlertClose} deleteId={tutorialId} />}
</MainCard>
......
......@@ -2,14 +2,15 @@ import { Column } from 'react-table';
export interface dataProps {
_id: number | string | undefined;
tutorialCode: String;
tutorialTitle: String;
tutorialImage: String;
status: Number;
createdBy: String;
updatedBy: String;
createdAt: Date;
updatedAt: Date;
tutorialCode: string;
tutorialTitle: string;
tutorialDescription: string;
tutorialImage: string;
status?: number;
createdBy?: string;
updatedBy?: string;
createdAt?: Date;
updatedAt?: Date;
taskItems: taskItemProps[]
}
......@@ -21,12 +22,13 @@ export interface ReactTableProps {
export interface tutorialProps {
_id: number | string | undefined;
tutorialCode: String;
tutorialTitle: String;
tutorialImage: String;
status: Number;
createdBy: String;
updatedBy: String;
tutorialCode: string;
tutorialTitle: string;
tutorialDescription: string;
tutorialImage: string;
status: number;
createdBy: string;
updatedBy: string;
createdAt: Date;
updatedAt: Date;
taskItems: taskItemProps[]
......@@ -34,9 +36,10 @@ export interface tutorialProps {
export interface taskItemProps {
_id: number | string | undefined;
title: String;
description: String;
howToDo: String;
referenceImage: String;
referenceVideo: String;
title: string;
description: string;
howToDo: string;
referenceImage: string;
referenceVideo: string;
taskItemMark: number;
}
\ No newline at end of file
.videoContainer {
position: relative;
width: 50%;
padding-top: 56.25%; /* 16:9 aspect ratio */
}
.futuristicVideo {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: none;
outline: none;
background-color: black;
opacity: 0.8;
filter: blur(4px);
transition: opacity 0.5s, filter 0.5s;
}
.futuristicVideo:hover {
opacity: 1;
filter: blur(0);
}
import { useRef, useState } from 'react';
import { Button, Grid } from '@mui/material';
import Webcam from 'react-webcam';
import { PauseCircleOutlined, PlayCircleOutlined } from '@ant-design/icons';
//@ts-ignore
const WebcamStreamCapture = ({ onVideoRecorded }) => {
const webcamRef = useRef(null);
const mediaRecorderRef = useRef(null);
const [capturing, setCapturing] = useState(false);
const [recordedChunks, setRecordedChunks] = useState([]);
const handleStartCaptureClick = () => {
setRecordedChunks([]);
setCapturing(true);
//@ts-ignore
mediaRecorderRef.current = new MediaRecorder(webcamRef.current.stream, {
mimeType: 'video/webm'
});
//@ts-ignore
mediaRecorderRef.current.addEventListener('dataavailable', handleDataAvailable);
//@ts-ignore
mediaRecorderRef.current.start();
};
//@ts-ignore
const handleDataAvailable = ({ data }) => {
if (data.size > 0) {
setRecordedChunks((prev) => prev.concat(data));
}
};
const handleStopCaptureClick = () => {
//@ts-ignore
mediaRecorderRef.current.stop();
setCapturing(false);
};
const handleDownload = () => {
if (recordedChunks.length) {
const blob = new Blob(recordedChunks, {
type: 'video/webm' // Use 'video/webm' to match MediaRecorder mimeType
});
const url = URL.createObjectURL(blob);
onVideoRecorded(url); // Pass the blob URL to the parent component
setRecordedChunks([]);
}
};
return (
<Grid container spacing={2}>
<Grid item xs={12}>
<center>
<Webcam audio={false} ref={webcamRef} style={{ width: '100%', maxWidth: '500px' }} />
</center>
</Grid>
<Grid item xs={12}>
<center>
{capturing ? (
<Button onClick={handleStopCaptureClick} startIcon={<PauseCircleOutlined />} color="error" variant="contained">
Stop Capture
</Button>
) : (
<Button onClick={handleStartCaptureClick} startIcon={<PlayCircleOutlined />} color="error" variant="contained">
Start Capture
</Button>
)}
{recordedChunks.length > 0 && (
<Button onClick={handleDownload} variant="contained" sx={{ ml: 1 }}>
Download
</Button>
)}
</center>
</Grid>
<Grid item xs={12}>
{recordedChunks.length > 0 && (
<center>
{/* @ts-ignore */}
<video
src={recordedChunks.length > 0 ? URL.createObjectURL(new Blob(recordedChunks, { type: 'video/webm' })) : undefined}
controls
autoPlay
/>
</center>
)}
</Grid>
</Grid>
);
};
export default WebcamStreamCapture;
// material-ui
// third-party
// project import
import MainCard from 'components/MainCard';
import ScrollX from 'components/ScrollX';
import {
Box,
Button,
ButtonGroup,
Card,
CardContent,
CardHeader,
Container,
Grid,
IconButton,
InputAdornment,
LinearProgress,
Paper,
Slider,
Stack,
TextField,
Typography
} from '@mui/material';
// layouts
// sections
import { useState } from 'react';
import { useSnackbar } from 'notistack';
import { CloudUploadOutlined, CopyOutlined, TranslationOutlined, VideoCameraOutlined } from '@ant-design/icons';
import { MuiFileInput } from 'mui-file-input';
import SignLanguageToTextService from '../../../services/SignLanguageToText.js';
import WebcamStreamCapture from './WebcamStreamCapture';
// assets
......@@ -15,15 +39,365 @@ import ScrollX from 'components/ScrollX';
// ==============================|| Process ||============================== //
const Process = () => {
return (
<>
<MainCard content={false}>
<ScrollX>
{/* content here */}
</ScrollX>
</MainCard>
</>
)
}
export default Process;
\ No newline at end of file
const [file, setFile] = useState<File | string | null>(null);
const [isUploadFile, setIsUploadFile] = useState<boolean | string | null>(true);
const [videoUrl, setVideoUrl] = useState('');
const [loading, setLoading] = useState(false);
const [value, setValue] = useState('');
const [speed, setSpeed] = useState(0);
const [recordedVideoUrl, setRecordedVideoUrl] = useState(null);
const handleDropSingleFile = (files: any) => {
if (files) {
setFile(
Object.assign(files, {
preview: URL.createObjectURL(files)
})
);
setVideoUrl(URL.createObjectURL(files));
}
};
const checkTranalationTypeForUpload = () => {
if (isUploadFile) {
return 'contained';
} else {
return 'outlined';
}
};
const checkTranalationTypeForRecord = () => {
if (!isUploadFile) {
return 'contained';
} else {
return 'outlined';
}
};
const { enqueueSnackbar } = useSnackbar();
const onCopy = (text: string) => {
if (text) {
navigator.clipboard.writeText(text);
enqueueSnackbar('Copied!', { variant: 'success' });
}
};
const handleChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
setValue(event.target.value);
};
// Video Upload
const translateSignLanguageToText = async () => {
if (file) {
setLoading(true);
const formData = new FormData();
//@ts-ignore
formData.append('video_request', file, file.name);
try {
const response = await SignLanguageToTextService.predictSignLanguageVideo(speed, formData);
if (response.status == 200) {
console.log(response.data);
setValue(response.data.predictions.join(''));
} else {
enqueueSnackbar('Something went Wrong!', { variant: 'error' });
}
setLoading(false);
} catch (error) {
console.log(error);
setLoading(false);
enqueueSnackbar('Something went Wrong!', { variant: 'error' });
}
} else {
enqueueSnackbar('Please select a file.', { variant: 'warning' });
}
};
const translateSignLanguageToTextRecord = async () => {
console.log('TEST TES ');
console.log(recordedVideoUrl);
if (recordedVideoUrl) {
setLoading(true);
const formData = new FormData();
//@ts-ignore
formData.append('video_request', recordedVideoUrl, recordedVideoUrl.name);
try {
const response = await SignLanguageToTextService.predictSignLanguageVideo(speed, formData);
if (response.status == 200) {
console.log(response.data);
setValue(response.data.predictions.join(''));
} else {
enqueueSnackbar('Something went Wrong!', { variant: 'error' });
}
setLoading(false);
} catch (error) {
console.log(error);
setLoading(false);
enqueueSnackbar('Something went Wrong!', { variant: 'error' });
}
} else {
enqueueSnackbar('Please record a video.', { variant: 'warning' });
}
};
function valuetext(value: number) {
setSpeed(value);
return `$${value}°C`;
}
const handleVideoRecorded = (url: any) => {
setRecordedVideoUrl(url);
};
return (
<>
<MainCard content={false}>
<ScrollX>
{/* content here */}
<Container
sx={{
padding: 2
}}
>
<ButtonGroup disableElevation variant="contained" aria-label="Disabled elevation buttons" sx={{ marginBottom: '10px' }}>
<Button
variant={checkTranalationTypeForUpload()}
startIcon={<CloudUploadOutlined />}
onClick={() => {
setIsUploadFile(true);
}}
>
Upload
</Button>
<Button
variant={checkTranalationTypeForRecord()}
startIcon={<VideoCameraOutlined />}
onClick={() => {
setIsUploadFile(false);
}}
>
Record
</Button>
</ButtonGroup>
{isUploadFile ? (
<Box sx={{ flexGrow: 1 }}>
<Card>
<CardHeader title="Upload a video containing Sign Language" />
<Grid container spacing={2}>
<Grid item xs={12} md={6}>
<Card sx={{ marginBottom: '10px', marginLeft: '10px' }}>
<CardContent>
{/* ! Important */}
{/* @ts-ignore */}
<MuiFileInput value={file} onChange={handleDropSingleFile} inputProps={{ accept: 'video/*' }} />
{/* {file && (
<Paper style={{ padding: '20px' }}>
<Typography variant="h5" align="center" gutterBottom>
Preview
</Typography>
<div style={{ marginTop: '20px', textAlign: 'center' }}>
<video src={videoUrl} width="400" controls />
</div>
</Paper>
)} */}
<Paper style={{ padding: '20px', marginTop: '15px' }}>
<Typography variant="h5" align="center" gutterBottom>
Preview
</Typography>
<div style={{ marginTop: '20px', textAlign: 'center' }}>
{file ? <video src={videoUrl} width="400" controls /> : <p>No Video Selected ...</p>}
</div>
</Paper>
</CardContent>
</Card>
</Grid>
<Grid item xs={12} md={6}>
<Card sx={{ p: 5, minHeight: 300, marginBottom: '10px', marginRight: '10px' }}>
<Box display="grid" gap={5}>
<Stack spacing={2}>
<Grid container spacing={2}>
<Grid item xs={12} md={6}>
<h3>Set Sign Speed </h3>
<Slider
defaultValue={30}
getAriaValueText={valuetext}
valueLabelDisplay="auto"
step={10}
marks
min={10}
max={110}
/>
<h4>Speed - {speed}</h4>
</Grid>
<Grid item xs={12} md={6} container direction="row" justifyContent="flex-end" alignItems="center">
<Button
variant="contained"
style={{ width: '200px', height: '60px', fontSize: '20px' }}
sx={{
mb: 3
}}
disabled={loading}
onClick={() => {
translateSignLanguageToText();
}}
endIcon={<TranslationOutlined />}
>
Translate
</Button>
</Grid>
</Grid>
{loading ? (
<Card>
<CardContent>
<LinearProgress />
<center>
<Typography variant="h5" component="div" sx={{ marginTop: 2 }}>
Loading...
</Typography>
</center>
</CardContent>
</Card>
) : (
<div>
<Typography variant="overline" sx={{ color: 'text.secondary' }}>
Translated Text
</Typography>
<TextField
fullWidth
value={value}
onChange={handleChange}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton onClick={() => onCopy(value)}>
<CopyOutlined />
</IconButton>
</InputAdornment>
)
}}
/>
</div>
)}
</Stack>
</Box>
</Card>
</Grid>
</Grid>
</Card>
</Box>
) : (
// Video Capture
<Box sx={{ flexGrow: 1 }}>
<Card>
<CardHeader title="Capture a video containing Sign Language" />
<Grid container spacing={2}>
{/* Paste Here */}
<Grid item xs={12} md={6}>
<Card sx={{ marginBottom: '10px', marginLeft: '10px' }}>
<CardContent>
<WebcamStreamCapture onVideoRecorded={handleVideoRecorded} />
</CardContent>
{recordedVideoUrl && (
<div>
<h2>Recorded Video</h2>
<video src={recordedVideoUrl} controls autoPlay />
</div>
)}
</Card>
</Grid>
<Grid item xs={12} md={6}>
<Card sx={{ p: 5, minHeight: 300, marginBottom: '10px', marginRight: '10px' }}>
<Box display="grid" gap={5}>
<Stack spacing={2}>
<Grid container spacing={2}>
<Grid item xs={12} md={6}>
<h3>Set Sign Speed </h3>
<Slider
defaultValue={30}
getAriaValueText={valuetext}
valueLabelDisplay="auto"
step={10}
marks
min={10}
max={110}
/>
<h4>Speed - {speed}</h4>
</Grid>
<Grid item xs={12} md={6} container direction="row" justifyContent="flex-end" alignItems="center">
<Button
variant="contained"
style={{ width: '200px', height: '60px', fontSize: '20px' }}
sx={{
mb: 3
}}
disabled={loading}
onClick={() => {
translateSignLanguageToTextRecord();
}}
endIcon={<TranslationOutlined />}
>
Translate
</Button>
</Grid>
</Grid>
{loading ? (
<Card>
<CardContent>
<LinearProgress />
<center>
<Typography variant="h5" component="div" sx={{ marginTop: 2 }}>
Loading...
</Typography>
</center>
</CardContent>
</Card>
) : (
<div>
<Typography variant="overline" sx={{ color: 'text.secondary' }}>
Translated Text
</Typography>
<TextField
fullWidth
value={value}
onChange={handleChange}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton onClick={() => onCopy(value)}>
<CopyOutlined />
</IconButton>
</InputAdornment>
)
}}
/>
</div>
)}
</Stack>
</Box>
</Card>
</Grid>
</Grid>
</Card>
</Box>
)}
</Container>
</ScrollX>
</MainCard>
</>
);
};
export default Process;
......@@ -27,12 +27,16 @@ const Dashboard = Loadable(lazy(() => import('pages/home/dashboard')));
const MemberManagementList = Loadable(lazy(() => import('pages/member-management/list/list')));
// render - user management page
const UserManagementList = Loadable(lazy(() => import('pages/user-management/list/list')));
const UserManagementList = Loadable(lazy(() => import('pages/user-management/list/list')));
// render - ssl translate process page
const SSLTranslateProcess = Loadable(lazy(() => import('pages/ssl-translate/process/process')));
// render - learning-management pages
const LearningDashboard = Loadable(lazy(() => import('pages/learning-management/dashboard')));
const LearningCurriculums = Loadable(lazy(() => import('pages/learning-management/learning-curriculums/list/list')));
const LearningCurriculumsSubscribed = Loadable(lazy(() => import('pages/learning-management/learning-curriculums-subscribed/list/list')));
const LearningCurriculumsSubscribedTutorial = Loadable(lazy(() => import('pages/learning-management/learning-curriculums-subscribed/tutorial/tutorial')));
const LearningLeadBoard = Loadable(lazy(() => import('pages/learning-management/learning-lead-board/list/list')));
const LearningFeedBack = Loadable(lazy(() => import('pages/learning-management/learning-feedback/list/list')));
......@@ -42,11 +46,17 @@ const CurriculumManagementList = Loadable(lazy(() => import('pages/parameter/cur
// render - parameter tutorial management page
const TutorialManagementList = Loadable(lazy(() => import('pages/parameter/tutorial-management/list/list')));
// render - ssl translate process page
const SSLTranslateProcess = Loadable(lazy(() => import('pages/ssl-translate/process/process')));
// render - Video to Sign Language page
const VideoTranslation = Loadable(lazy(() => import('pages/video-to-sign-language/VideoTranslate/VideoTranslate')));
const VideoTranslation = Loadable(lazy(() => import('pages/video-to-sign-language/VideoTranslate/VideoTranslate')));
// render - audio-detection page
const AudioDetection = Loadable(lazy(() => import('pages/emotion-detection/emotion-audio-detection/list/list')));
// render - video-detection page
const VideoDetection = Loadable(lazy(() => import('pages/emotion-detection/emotion-video-detection/list/list')));
// ==============================|| MAIN ROUTING ||============================== //
......@@ -97,6 +107,21 @@ const MainRoutes = {
}
]
},
{
path: 'emotion-detection',
children: [
{
path: 'audio-detection',
element: <AudioDetection />
},
{
path: 'video-detection',
element: <VideoDetection />
}
]
},
{
path: 'video-to-sign-language',
children: [
......@@ -121,6 +146,10 @@ const MainRoutes = {
path: 'curriculums-subscribed',
element: <LearningCurriculumsSubscribed />
},
{
path: 'curriculums-subscribed-tutorial',
element: <LearningCurriculumsSubscribedTutorial />
},
{
path: 'lead-board',
element: <LearningLeadBoard />
......
import React, { useEffect } from 'react';
// third party
import { useInView } from 'react-intersection-observer';
import { motion, useAnimation } from 'framer-motion';
// =============================|| LANDING - FADE IN ANIMATION ||============================= //
function Animation({ children, variants }: { children: React.ReactElement; variants: any }) {
const controls = useAnimation();
const [ref, inView] = useInView();
useEffect(() => {
if (inView) {
controls.start('visible');
}
}, [controls, inView]);
return (
<motion.div
ref={ref}
animate={controls}
initial="hidden"
transition={{
x: {
type: 'spring',
stiffness: 150,
damping: 30,
duration: 0.5
},
opacity: { duration: 1 }
}}
variants={variants}
>
{children}
</motion.div>
);
}
export default Animation;
// material-ui
import {
Box,
Grid,
LinearProgress,
Stack,
Typography
} from '@mui/material';
import { useTheme } from '@mui/material/styles';
// project import
import TutorialSection from './TutorialSection';
// types
import MainCard from 'components/MainCard';
import { ThemeDirection } from 'types/config';
import { curriculumTypeUserProgress } from "types/userProgress";
// assets
import { CheckCircleOutlined, UnorderedListOutlined } from '@ant-design/icons';
import Reader from 'assets/images/analytics/reader.svg';
import ReportCard from 'components/cards/statistics/ReportCard';
// ==============================|| Curriculum - Section ||============================== //
const CurriculumSection = ({ curriculum }: { curriculum: curriculumTypeUserProgress }) => {
const theme = useTheme();
return (
<>
<Stack spacing={2} sx={{ padding: 2 }}>
<MainCard title="Overview">
<Grid container spacing={2}>
<Grid
item
xs={7}
sm={7}
sx={{
bgcolor: `${theme.palette.primary.main}`,
position: 'relative',
p: 2.75,
borderRadius: { xs: 2, sm: '8px 0px 0px 8px' },
overflow: 'hidden'
}}
>
<Stack>
<Typography variant="h5" color="white">
{curriculum.curriculumDescription}
</Typography>
<Typography color={theme.palette.grey[0]} variant="caption" sx={{ maxWidth: '55%', pt: 1 }}>
Your learning capacity is 80% as daily analytics
</Typography>
<Typography variant="h4" color="white" sx={{ pt: 8, pb: 1, zIndex: 1 }}>
{(curriculum.curriculumMarkUser / curriculum.curriculumMark) * 100}% Completed
</Typography>
<Box sx={{ maxWidth: '60%' }}>
<LinearProgress variant="determinate" color="success" value={(curriculum.curriculumMarkUser / curriculum.curriculumMark) * 100} />
</Box>
<Box
sx={{
position: 'absolute',
bottom: -7,
right: 0,
...(theme.direction === ThemeDirection.RTL && { transform: { xs: 'rotateY(180deg)', sm: 'inherit' } })
}}
>
<img alt="reder" src={Reader} />
</Box>
</Stack>
</Grid>
<Grid md={5} item>
<Grid container spacing={2}>
<Grid md={12} item>
<ReportCard primary={`LEVEL - ${curriculum.curriculumLevel}`} secondary="Curriculum Level" color={theme.palette.error.main} iconPrimary={UnorderedListOutlined} />
</Grid>
<Grid md={12} item>
<ReportCard primary={`Pass Mark - ${curriculum.curriculumMark}`} secondary="Curriculum Pass Mark" color={theme.palette.success.dark} iconPrimary={CheckCircleOutlined} />
</Grid>
</Grid>
</Grid>
</Grid>
</MainCard>
<MainCard title="Tutorials">
<Grid container spacing={2}>
{curriculum.tutorials.map((tutorial, index) => {
return (<TutorialSection tutorial={tutorial!} />)
})}
</Grid>
</MainCard>
</Stack>
</>
);
};
export default CurriculumSection;
import { useState } from 'react';
import { useNavigate } from 'react-router';
// material-ui
import {
Box,
Button,
Grid,
Typography
} from '@mui/material';
// project import
import Animation from 'sections/learning-management/learning-curriculums-subscribed/Animation';
// types
import { tutorialTypeUserProgress } from "types/userProgress";
// assets
import { PlaySquareOutlined } from '@ant-design/icons';
import AnimateButton from 'components/@extended/AnimateButton';
import MainCard from 'components/MainCard';
// ==============================|| Tutorial - Section ||============================== //
const TutorialSection = ({ tutorial }: { tutorial: tutorialTypeUserProgress }) => {
let navigation = useNavigate()
const [desc, setDesc] = useState(tutorial.tutorialDescription?.slice(0, 100))
const [readMore, setReadMore] = useState(false)
return (
<>
<Grid item md={3}>
<Animation
variants={{
visible: { opacity: 1 },
hidden: { opacity: 0 }
}}
>
<MainCard contentSX={{ p: 3 }}>
<Grid container spacing={1.5}>
<Grid item xs={12}>
<Typography variant="h4" sx={{ fontWeight: 600, mt: 2 }}>
{tutorial.tutorialTitle}
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="body1" color="secondary" sx={{ textAlign: "justify" }}>
{desc}
<span style={{ fontWeight: "bold", cursor: "pointer" }}
onClick={() => {
if (!readMore) {
setDesc(tutorial.tutorialDescription)
setReadMore(true)
} else {
setDesc(tutorial.tutorialDescription?.slice(0, 100))
setReadMore(false)
}
}} color="secondary">
{readMore ? "Show Less" : "...Read More"}
</span>
</Typography>
</Grid>
<Grid item xs={12}>
<Box sx={{ display: 'inline-block' }}>
<AnimateButton>
<Button
variant="outlined"
endIcon={<PlaySquareOutlined />}
sx={{ my: 2 }}
onClick={() => { navigation(`/learning-management/curriculums-subscribed-tutorial`) }}
>
Start Tutorial
</Button>
</AnimateButton>
</Box>
</Grid>
<Grid item xs={12} sx={{ '& img': { mb: -3.75, width: `calc( 100% + 24px)` } }}>
<img src={tutorial.tutorialImage} alt="feature" />
</Grid>
</Grid>
</MainCard>
</Animation>
</Grid>
</>
)
}
export default TutorialSection;
\ No newline at end of file
// material-ui
import { CardContent, Grid, Skeleton, Stack, Avatar } from '@mui/material';
// project import
import MainCard from 'components/MainCard';
// assets
import { ContactsOutlined } from '@ant-design/icons';
// ===========================|| SKELETON - USER EMPTY CARD ||=========================== //
const UserCard = () => {
return (
<MainCard
border={false}
content={false}
boxShadow
sx={{ boxShadow: `rgba(50, 50, 93, 0.25) 0px 13px 27px -5px, rgba(0, 0, 0, 0.3) 0px 8px 16px -8px`, borderRadius: 2 }}
>
<CardContent sx={{ p: 2 }}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Stack flexDirection="row" alignItems="center">
<Avatar>
<ContactsOutlined style={{ visibility: 'inherit' }} />
</Avatar>
<Stack sx={{ width: '100%', pl: 2.5 }}>
<Skeleton animation={false} height={20} width="80%" />
<Skeleton animation={false} height={20} width="40%" />
</Stack>
</Stack>
</Grid>
<Grid item xs={12}>
<Skeleton animation={false} height={20} width={45} />
<Skeleton animation={false} height={20} />
<Stack direction="row" alignItems="center" spacing={1}>
<Skeleton animation={false} height={20} width={90} />
<Skeleton animation={false} height={20} width={38} />
</Stack>
</Grid>
<Grid item xs={12}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Grid container spacing={1}>
<Grid item>
<Skeleton animation={false} height={20} width={40} />
</Grid>
<Grid item>
<Skeleton animation={false} height={17} width={20} />
</Grid>
</Grid>
<Skeleton animation={false} height={32} width={47} />
</Stack>
</Grid>
</Grid>
</CardContent>
</MainCard>
);
};
export default UserCard;
// material-ui
import { Box, Grid, Stack, Typography } from '@mui/material';
// project import
import CurriculumCard from './CurriculumCard';
interface Props {
title: string;
}
// ==============================|| EMPTY STATE ||============================== //
const EmptyCurriculumCard = ({ title }: Props) => {
return (
<Grid container spacing={3}>
<Grid item xs={12}>
<Box
sx={{
p: { xs: 2.5, sm: 6 },
height: `calc(100vh - 192px)`,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
bgcolor: 'transparent'
}}
>
<Grid container direction="column" justifyContent="center" alignItems="center">
<Grid item>
<Box sx={{ ml: -9, mb: { xs: -8, sm: -5 } }}>
<Box sx={{ position: 'relative' }}>
<CurriculumCard />
</Box>
<Box sx={{ position: 'relative', top: -120, left: 72 }}>
<CurriculumCard />
</Box>
</Box>
</Grid>
<Grid item>
<Stack spacing={1}>
<Typography align="center" variant="h4">
{title}
</Typography>
</Stack>
</Grid>
</Grid>
</Box>
</Grid>
</Grid>
);
};
export default EmptyCurriculumCard;
import React, { useEffect } from 'react';
// third party
import { useInView } from 'react-intersection-observer';
import { motion, useAnimation } from 'framer-motion';
// =============================|| LANDING - FADE IN ANIMATION ||============================= //
function Animation({ children, variants }: { children: React.ReactElement; variants: any }) {
const controls = useAnimation();
const [ref, inView] = useInView();
useEffect(() => {
if (inView) {
controls.start('visible');
}
}, [controls, inView]);
return (
<motion.div
ref={ref}
animate={controls}
initial="hidden"
transition={{
x: {
type: 'spring',
stiffness: 150,
damping: 30,
duration: 0.5
},
opacity: { duration: 1 }
}}
variants={variants}
>
{children}
</motion.div>
);
}
export default Animation;
import { useState } from 'react';
import { useNavigate } from 'react-router';
// material-ui
import {
Box,
Button,
Grid,
Typography
} from '@mui/material';
// third-party
// project import
import MainCard from 'components/MainCard';
// assets
import { PlusOutlined, SendOutlined } from '@ant-design/icons';
import AnimateButton from 'components/@extended/AnimateButton';
import { curriculumType } from 'types/curriculum';
import Animation from './Animation';
import CurriculumPreview from './CurriculumPreview';
// types
// ==============================|| CURRICULUM - CARD ||============================== //
const CurriculumCard = ({ curriculum }: { curriculum: curriculumType }) => {
const navigate = useNavigate()
const [open, setOpen] = useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const [desc, setDesc] = useState(curriculum.curriculumDescription?.slice(0, 100))
const [readMore, setReadMore] = useState(false)
return (
<>
<Animation
variants={{
visible: { opacity: 1 },
hidden: { opacity: 0 }
}}
>
<MainCard contentSX={{ p: 3 }}>
<Grid container spacing={1.5}>
<Grid item xs={12}>
<Typography variant="h3" sx={{ fontWeight: 600, mt: 2 }}>
{curriculum.curriculumTitle}
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="body1" color="secondary" sx={{ textAlign: "justify" }}>
{desc}
<span style={{ fontWeight: "bold", cursor: "pointer" }}
onClick={() => {
if (!readMore) {
setDesc(curriculum.curriculumDescription)
setReadMore(true)
} else {
setDesc(curriculum.curriculumDescription?.slice(0, 100))
setReadMore(false)
}
}} color="secondary">
{readMore ? "Show Less" : "...Read More"}
</span>
</Typography>
</Grid>
<Grid item xs={6}>
<Box sx={{ display: 'inline-block' }}>
<AnimateButton>
<Button
fullWidth
variant="outlined"
endIcon={<PlusOutlined />}
sx={{ my: 2, width: "100%" }}
onClick={() => { navigate(`/learning-management/curriculums-subscribed`) }}
color='success'
>
Follow Curriculum
</Button>
</AnimateButton>
</Box>
</Grid>
<Grid item xs={6}>
<Box sx={{ display: 'inline-block' }}>
<AnimateButton>
<Button
fullWidth
variant="outlined"
endIcon={<SendOutlined />}
sx={{ my: 2, width: "100%" }}
onClick={handleClickOpen}
>
Preview Curriculum
</Button>
</AnimateButton>
</Box>
</Grid>
<Grid item xs={12} sx={{ '& img': { marginBottom: '-3.75px', width: '100%', height: '456px', objectFit: 'cover' } }}>
<img src={curriculum.curriculumImage} alt="feature" />
</Grid>
</Grid>
</MainCard>
</Animation>
{/* edit curriculum dialog */}
<CurriculumPreview curriculum={curriculum} open={open} onClose={handleClose} />
</>
);
};
export default CurriculumCard;
import { useState } from 'react';
// material-ui
import {
Accordion, AccordionDetails, AccordionSummary,
Box,
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
Grid,
List,
ListItem,
ListItemAvatar,
ListItemText,
Stack,
Tooltip,
Typography
} from '@mui/material';
import { useTheme } from '@mui/material/styles';
// third-party
import { PDFDownloadLink } from '@react-pdf/renderer';
// project import
import Avatar from 'components/@extended/Avatar';
import IconButton from 'components/@extended/IconButton';
import { PopupTransition } from 'components/@extended/Transitions';
import MainCard from 'components/MainCard';
import SimpleBar from 'components/third-party/SimpleBar';
// assets
import { DownloadOutlined, TagOutlined } from '@ant-design/icons';
import curriculumLevels from 'data/curriculumLevels';
import { curriculumType } from 'types/curriculum';
// types
// ==============================|| Curriculum - CARD PREVIEW ||============================== //
export default function CurriculumPreview({ curriculum, open, onClose }: { curriculum: curriculumType; open: boolean; onClose: () => void }) {
const theme = useTheme();
const [expanded, setExpanded] = useState<string | false>('panel0');
const handleChange = (panel: string) => (event: React.SyntheticEvent, newExpanded: boolean) => {
setExpanded(newExpanded ? panel : false);
};
return (
<>
<Dialog
open={open}
TransitionComponent={PopupTransition}
keepMounted
onClose={onClose}
aria-describedby="alert-dialog-slide-description"
sx={{ '& .MuiDialog-paper': { width: 1024, maxWidth: 1, m: { xs: 1.75, sm: 2.5, md: 4 } } }}
>
<Box id="PopupPrint" sx={{ px: { xs: 2, sm: 3, md: 5 }, py: 1 }}>
<DialogTitle sx={{ px: 0 }}>
<List sx={{ width: 1, p: 0 }}>
<ListItem
disablePadding
secondaryAction={
<Stack direction="row" alignItems="center" justifyContent="center" spacing={0}>
<Tooltip title="Export">
<PDFDownloadLink
document={<></>} fileName={`${curriculum.curriculumCode}-${curriculum.curriculumTitle}.pdf`}
// document={<ListCard customer={customer} />} fileName={`Customer-${customer.fatherName}.pdf`}
>
<IconButton color="secondary">
<DownloadOutlined />
</IconButton>
</PDFDownloadLink>
</Tooltip>
{/* <Tooltip title="Edit">
<IconButton color="secondary" onClick={handleAdd}>
<EditOutlined />
</IconButton>
</Tooltip>
<Tooltip title="Delete" onClick={handleClose}>
<IconButton color="error">
<DeleteOutlined />
</IconButton>
</Tooltip> */}
</Stack>
}
>
<ListItemAvatar sx={{ mr: 0.75 }}>
<Avatar alt={curriculum.curriculumTitle} size="lg" src={curriculum.curriculumImage} />
</ListItemAvatar>
<ListItemText
primary={<Typography variant="h5">{curriculum.curriculumTitle}</Typography>}
secondary={<Typography color="secondary"> {curriculumLevels.find(level => level.id === curriculum.curriculumLevel)?.description || ""}</Typography>}
/>
</ListItem>
</List>
</DialogTitle>
<DialogContent dividers sx={{ px: 0 }}>
<SimpleBar sx={{ height: 'calc(100vh - 390px)' }}>
<Grid container spacing={3}>
<Grid item xs={12} sm={8} xl={12}>
<Grid container spacing={2.25}>
<Grid item xs={12}>
<MainCard title="Overview">
<Typography>
{curriculum.curriculumDescription}
</Typography>
</MainCard>
</Grid>
<Grid item xs={12}>
<MainCard title="Tutorials">
<Box
sx={{
'& .MuiAccordion-root': {
borderColor: theme.palette.divider,
'& .MuiAccordionSummary-root': {
bgcolor: 'transparent',
flexDirection: 'row',
'&:focus-visible': {
bgcolor: 'primary.lighter'
}
},
'& .MuiAccordionDetails-root': {
borderColor: theme.palette.divider
},
'& .Mui-expanded': {
color: theme.palette.primary.main
}
}
}}
>
{curriculum.tutorials?.map((tutorial, index) => {
return (
<>
<Accordion expanded={expanded === `panel${index}`} onChange={handleChange(`panel${index}`)}>
<AccordionSummary aria-controls={`panel${index}d-content`} id={`panel${index}d-header`}>
<Stack direction="row" spacing={1.5} alignItems="center">
<TagOutlined />
<Typography variant="h6">{tutorial.tutorialTitle}</Typography>
</Stack>
</AccordionSummary>
<AccordionDetails>
<Stack spacing={2}>
<Typography variant="h5">{tutorial.tutorialDescription}</Typography>
</Stack>
</AccordionDetails>
</Accordion>
</>
)
})}
</Box>
</MainCard>
</Grid>
</Grid>
</Grid>
</Grid>
</SimpleBar>
</DialogContent>
<DialogActions>
<Button color="error" onClick={onClose}>
Close
</Button>
</DialogActions>
</Box>
</Dialog>
</>
);
}
// material-ui
import { CardContent, Grid, Skeleton, Stack, Avatar } from '@mui/material';
// project import
import MainCard from 'components/MainCard';
// assets
import { ContactsOutlined } from '@ant-design/icons';
// ===========================|| SKELETON - USER EMPTY CARD ||=========================== //
const UserCard = () => {
return (
<MainCard
border={false}
content={false}
boxShadow
sx={{ boxShadow: `rgba(50, 50, 93, 0.25) 0px 13px 27px -5px, rgba(0, 0, 0, 0.3) 0px 8px 16px -8px`, borderRadius: 2 }}
>
<CardContent sx={{ p: 2 }}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Stack flexDirection="row" alignItems="center">
<Avatar>
<ContactsOutlined style={{ visibility: 'inherit' }} />
</Avatar>
<Stack sx={{ width: '100%', pl: 2.5 }}>
<Skeleton animation={false} height={20} width="80%" />
<Skeleton animation={false} height={20} width="40%" />
</Stack>
</Stack>
</Grid>
<Grid item xs={12}>
<Skeleton animation={false} height={20} width={45} />
<Skeleton animation={false} height={20} />
<Stack direction="row" alignItems="center" spacing={1}>
<Skeleton animation={false} height={20} width={90} />
<Skeleton animation={false} height={20} width={38} />
</Stack>
</Grid>
<Grid item xs={12}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Grid container spacing={1}>
<Grid item>
<Skeleton animation={false} height={20} width={40} />
</Grid>
<Grid item>
<Skeleton animation={false} height={17} width={20} />
</Grid>
</Grid>
<Skeleton animation={false} height={32} width={47} />
</Stack>
</Grid>
</Grid>
</CardContent>
</MainCard>
);
};
export default UserCard;
// material-ui
import { Box, Grid, Stack, Typography } from '@mui/material';
// project import
import CurriculumCard from './CurriculumCard';
interface Props {
title: string;
}
// ==============================|| EMPTY STATE ||============================== //
const EmptyCurriculumCard = ({ title }: Props) => {
return (
<Grid container spacing={3}>
<Grid item xs={12}>
<Box
sx={{
p: { xs: 2.5, sm: 6 },
height: `calc(100vh - 192px)`,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
bgcolor: 'transparent'
}}
>
<Grid container direction="column" justifyContent="center" alignItems="center">
<Grid item>
<Box sx={{ ml: -9, mb: { xs: -8, sm: -5 } }}>
<Box sx={{ position: 'relative' }}>
<CurriculumCard />
</Box>
<Box sx={{ position: 'relative', top: -120, left: 72 }}>
<CurriculumCard />
</Box>
</Box>
</Grid>
<Grid item>
<Stack spacing={1}>
<Typography align="center" variant="h4">
{title}
</Typography>
</Stack>
</Grid>
</Grid>
</Box>
</Grid>
</Grid>
);
};
export default EmptyCurriculumCard;
import { useState } from 'react';
// material-ui
import {
Autocomplete,
Box,
Button,
DialogActions,
DialogContent,
DialogTitle,
Divider,
FormHelperText,
Grid,
InputLabel,
Stack,
TextField,
Tooltip
} from '@mui/material';
import { useTheme } from '@mui/material/styles';
import { LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
// third-party
import { FieldArray, Form, FormikProvider, FormikValues, useFormik } from 'formik';
import _ from 'lodash';
import * as Yup from 'yup';
// project imports
import IconButton from 'components/@extended/IconButton';
// assets
import { DeleteFilled } from '@ant-design/icons';
import MainCard from 'components/MainCard';
import curriculumLevels, { CurriculumLevelsType } from 'data/curriculumLevels';
import AlertCurriculumDelete from './AlertCurriculumDelete';
// types
// constant
const getInitialValues = (curriculum: FormikValues | null) => {
const newCurriculum = {
_id: undefined,
curriculumCode: "",
curriculumLevel: undefined,
curriculumTitle: "",
curriculumDescription: "",
curriculumImage: "",
tutorials: [''],
}
if (curriculum) {
return _.merge({}, newCurriculum, curriculum);
}
return newCurriculum;
};
// ==============================|| CUSTOMER ADD / EDIT ||============================== //
export interface Props {
curriculum?: {
_id: number | string | undefined;
curriculumCode: string;
curriculumLevel: number;
curriculumTitle: string;
curriculumDescription: string;
curriculumImage: string;
tutorials: string[];
status: number;
createdBy: string;
updatedBy: string;
createdAt: Date;
updatedAt: Date;
};
onCancel: () => void;
}
const AddEditCurriculum = ({ curriculum, onCancel }: Props) => {
const theme = useTheme();
const isCreating = !curriculum;
const CurriculumSchema = Yup.object().shape({});
const [openAlert, setOpenAlert] = useState(false);
const handleAlertClose = () => {
setOpenAlert(!openAlert);
onCancel();
};
const formik = useFormik({
initialValues: getInitialValues(curriculum!),
validationSchema: CurriculumSchema,
enableReinitialize: true,
onSubmit: (values, { setSubmitting, resetForm }) => {
try {
if (curriculum) {
// PUT API
} else {
// POST API
}
resetForm()
setSubmitting(false);
onCancel();
} catch (error) {
console.error(error);
}
}
});
const { errors, touched, handleSubmit, isSubmitting, getFieldProps, values } = formik;
// const { handleSubmit, isSubmitting } = formik;
return (
<>
<FormikProvider value={formik}>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<Form autoComplete="off" noValidate onSubmit={handleSubmit}>
<DialogTitle>{curriculum ? 'Edit Curriculum' : 'New Curriculum'}</DialogTitle>
<Divider />
<DialogContent sx={{ p: 2.5 }}>
<Grid container spacing={3}>
<Grid item xs={12} md={12}>
<Grid container spacing={3}>
<Grid item xs={4}>
<Stack spacing={1.25}>
<InputLabel htmlFor="curriculumCode">Curriculum Code</InputLabel>
<TextField
fullWidth
id="curriculumCode"
placeholder="Enter Curriculum Code"
{...getFieldProps('curriculumCode')}
error={Boolean(touched.curriculumCode && errors.curriculumCode)}
helperText={touched.curriculumCode && errors.curriculumCode}
/>
</Stack>
</Grid>
<Grid item xs={4}>
<Stack spacing={1.25}>
<InputLabel htmlFor="curriculumLevel">Curriculum Level</InputLabel>
<Autocomplete
fullWidth
id="curriculumLevel"
value={curriculumLevels.find((option) => option.id === formik.values.curriculumLevel) || null}
onChange={(event: any, newValue: CurriculumLevelsType | null) => {
formik.setFieldValue('curriculumLevel', newValue?.id);
}}
options={curriculumLevels}
getOptionLabel={(item) => `${item.description}`}
renderInput={(params) => {
return (
<TextField
{...params}
placeholder="Select Curriculum Level"
sx={{ '& .MuiAutocomplete-input.Mui-disabled': { WebkitTextFillColor: theme.palette.text.primary } }}
/>
)
}}
/>
{formik.touched.curriculumLevel && formik.errors.curriculumLevel && (
<FormHelperText error id="helper-text-curriculumLevel">
{formik.errors.curriculumLevel}
</FormHelperText>
)}
</Stack>
</Grid>
<Grid item xs={4}>
<Stack spacing={1.25}>
<InputLabel htmlFor="curriculumTitle">Curriculum Title</InputLabel>
<TextField
fullWidth
id="curriculumTitle"
placeholder="Enter Curriculum Title"
{...getFieldProps('curriculumTitle')}
error={Boolean(touched.curriculumTitle && errors.curriculumTitle)}
helperText={touched.curriculumTitle && errors.curriculumTitle}
/>
</Stack>
</Grid>
<Grid item xs={12}>
<Stack spacing={1.25}>
<InputLabel htmlFor="curriculumImage">Curriculum Image</InputLabel>
<TextField
fullWidth
id="curriculumImage"
placeholder="Enter Curriculum Image"
{...getFieldProps('curriculumImage')}
error={Boolean(touched.curriculumImage && errors.curriculumImage)}
helperText={touched.curriculumImage && errors.curriculumImage}
/>
</Stack>
</Grid>
<Grid item xs={12}>
<Stack spacing={1.25}>
<InputLabel htmlFor="curriculumDescription">Curriculum Description</InputLabel>
<TextField
multiline
rows={2}
fullWidth
id="curriculumDescription"
placeholder="Enter Curriculum Description"
{...getFieldProps('curriculumDescription')}
error={Boolean(touched.curriculumDescription && errors.curriculumDescription)}
helperText={touched.curriculumDescription && errors.curriculumDescription}
/>
</Stack>
</Grid>
<Grid item xs={12}>
<FieldArray name="tutorials">
{({ insert, remove, push, }) => {
return (
<>
<MainCard content={false} title="Tutorials" sx={{ '& .MuiInputLabel-root': { fontSize: '0.875rem' } }}
secondary={
<Button
style={{
float: "right"
}}
onClick={() => push("")}
variant="contained"
>
Add Tutorial
</Button>
}
>
<Box sx={{ p: 2.5 }}>
<Grid container spacing={3}>
{values.tutorials.length > 0 &&
values.tutorials.map((item, index) => {
return (
<>
<Grid item xs={11}>
<Stack spacing={1.25}>
<InputLabel htmlFor={`tutorials.${index}`}>Tutorial</InputLabel>
<Autocomplete
fullWidth
id={`tutorials.${index}`}
// value={tutorials.find((option) => option.id === formik.values.curriculumLevel) || null}
// onChange={(event: any, newValue: CurriculumLevelsType | null) => {
// formik.setFieldValue(`tutorials.${index}`, newValue?.id);
// }}
options={[]}
// getOptionLabel={(item) => `${item.description}`}
renderInput={(params) => {
return (
<TextField
{...params}
placeholder="Select Tutorial"
sx={{ '& .MuiAutocomplete-input.Mui-disabled': { WebkitTextFillColor: theme.palette.text.primary } }}
/>
)
}}
/>
{/* {formik.touched.curriculumLevel && formik.errors.curriculumLevel && (
<FormHelperText error id="helper-text-curriculumLevel">
{formik.errors.curriculumLevel}
</FormHelperText>
)} */}
</Stack>
</Grid>
<Grid item xs={1} style={{ marginTop: "4%" }}>
<Tooltip title="Delete Nutrition" placement="right-start">
<IconButton onClick={() => { remove(index) }} size="large" color="error">
<DeleteFilled />
</IconButton>
</Tooltip>
</Grid>
</>
)
})
}
</Grid >
</Box>
</MainCard>
</>
)
}}
</FieldArray>
</Grid>
</Grid>
</Grid>
</Grid>
</DialogContent>
<Divider />
<DialogActions sx={{ p: 2.5 }}>
<Grid container justifyContent="space-between" alignItems="center">
<Grid item>
{!isCreating && (
<Tooltip title="Delete Tutorial" placement="top">
<IconButton onClick={() => setOpenAlert(true)} size="large" color="error">
<DeleteFilled />
</IconButton>
</Tooltip>
)}
</Grid>
<Grid item>
<Stack direction="row" spacing={2} alignItems="center">
<Button color="error" onClick={onCancel}>
Cancel
</Button>
<Button type="submit" variant="contained" disabled={isSubmitting}>
{curriculum ? 'Edit' : 'Add'}
</Button>
</Stack>
</Grid>
</Grid>
</DialogActions>
</Form>
</LocalizationProvider>
</FormikProvider>
{!isCreating && <AlertCurriculumDelete title={""} open={openAlert} handleClose={handleAlertClose} deleteId={curriculum._id} />}
</>
);
};
export default AddEditCurriculum;
......@@ -13,7 +13,7 @@ interface Props {
title: string;
open: boolean;
handleClose: (status: boolean) => void;
deleteId: number | null;
deleteId: number | string | undefined;
}
// ==============================|| Curriculum - DELETE ||============================== //
......
import { useState } from 'react';
// material-ui
import {
Accordion,
AccordionDetails,
Box,
Button,
DialogActions,
DialogContent,
DialogTitle,
Divider,
Grid,
Stack,
Tooltip,
Typography
} from '@mui/material';
import { useTheme } from '@mui/material/styles';
import { LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
// third-party
import { FieldArray, Form, FormikProvider, FormikValues, useFormik } from 'formik';
import _ from 'lodash';
import * as Yup from 'yup';
// project imports
import IconButton from 'components/@extended/IconButton';
// assets
import { DeleteFilled } from '@ant-design/icons';
import { AccordionSummary, InputLabel, TextField } from '@mui/material';
import MainCard from 'components/MainCard';
import AlertTutorialDelete from './AlertTutorialDelete';
// types
// constant
const getInitialValues = (tutorial: FormikValues | null) => {
const newTutorial = {
_id: undefined,
tutorialCode: "",
tutorialTitle: "",
tutorialImage: "",
tutorialDescription: "",
taskItems: [
{
_id: undefined,
title: "",
description: "",
howToDo: "",
referenceImage: "",
referenceVideo: "",
}
]
}
if (tutorial) {
return _.merge({}, newTutorial, tutorial);
}
return newTutorial;
};
// ==============================|| CUSTOMER ADD / EDIT ||============================== //
export interface Props {
tutorial?: {
_id: number | string | undefined
tutorialCode: string;
tutorialTitle: string;
tutorialDescription: string;
tutorialImage: string;
status: number;
createdBy: string;
updatedBy: string;
createdAt: Date;
updatedAt: Date;
taskItems: taskItemProps[]
};
onCancel: () => void;
}
export interface taskItemProps {
_id: number | string | undefined;
title: string;
description: string;
howToDo: string;
referenceImage: string;
referenceVideo: string;
}
const AddEditTutorial = ({ tutorial, onCancel }: Props) => {
const theme = useTheme();
const isCreating = !tutorial;
const TutorialSchema = Yup.object().shape({});
const [openAlert, setOpenAlert] = useState(false);
const handleAlertClose = () => {
setOpenAlert(!openAlert);
onCancel();
};
const formik = useFormik({
initialValues: getInitialValues(tutorial!),
validationSchema: TutorialSchema,
enableReinitialize: true,
onSubmit: (values, { setSubmitting, resetForm }) => {
try {
if (tutorial) {
// PUT API
} else {
// POST API
}
resetForm()
setSubmitting(false);
onCancel();
} catch (error) {
console.error(error);
}
}
});
const { errors, touched, handleSubmit, isSubmitting, getFieldProps, values } = formik;
// const { handleSubmit, isSubmitting } = formik;
//accordion handlers
const [expanded, setExpanded] = useState<string | false>('panel-1');
const handleChange = (panel: string) => (event: React.SyntheticEvent, newExpanded: boolean) => {
setExpanded(newExpanded ? panel : false);
};
return (
<>
<FormikProvider value={formik}>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<Form autoComplete="off" noValidate onSubmit={handleSubmit}>
<DialogTitle>{tutorial ? 'Edit Tutorial' : 'New Tutorial'}</DialogTitle>
<Divider />
<DialogContent sx={{ p: 2.5 }}>
<Grid container spacing={3}>
<Grid item xs={12} md={12}>
<Grid container spacing={3}>
<Grid item xs={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="tutorialCode">Tutorial Code</InputLabel>
<TextField
fullWidth
id="tutorialCode"
placeholder="Enter Tutorial Code"
{...getFieldProps('tutorialCode')}
error={Boolean(touched.tutorialCode && errors.tutorialCode)}
helperText={touched.tutorialCode && errors.tutorialCode}
/>
</Stack>
</Grid>
<Grid item xs={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor="tutorialTitle">Tutorial Title</InputLabel>
<TextField
fullWidth
id="tutorialTitle"
placeholder="Enter Tutorial Title"
{...getFieldProps('tutorialTitle')}
error={Boolean(touched.tutorialTitle && errors.tutorialTitle)}
helperText={touched.tutorialTitle && errors.tutorialTitle}
/>
</Stack>
</Grid>
<Grid item xs={12}>
<Stack spacing={1.25}>
<InputLabel htmlFor="tutorialImage">Tutorial Image</InputLabel>
<TextField
fullWidth
id="tutorialImage"
placeholder="Enter Tutorial Image"
{...getFieldProps('tutorialImage')}
error={Boolean(touched.tutorialImage && errors.tutorialImage)}
helperText={touched.tutorialImage && errors.tutorialImage}
/>
</Stack>
</Grid>
<Grid item xs={12}>
<Stack spacing={1.25}>
<InputLabel htmlFor="tutorialDescription">Tutorial Description</InputLabel>
<TextField
multiline
rows={2}
fullWidth
id="tutorialDescription"
placeholder="Enter Tutorial Description"
{...getFieldProps('tutorialDescription')}
error={Boolean(touched.tutorialDescription && errors.tutorialDescription)}
helperText={touched.tutorialDescription && errors.tutorialDescription}
/>
</Stack>
</Grid>
<Grid item xs={12}>
<FieldArray name="taskItems">
{({ insert, remove, push, }) => {
return (
<>
<MainCard content={false} title="Tasks" sx={{ '& .MuiInputLabel-root': { fontSize: '0.875rem' } }}
secondary={
<Button
style={{
float: "right"
}}
onClick={() => push(
{
_id: undefined,
title: "",
description: "",
howToDo: "",
referenceImage: "",
referenceVideo: ""
}
)}
variant="contained"
>
Add Task
</Button>
}
>
<Box sx={{ p: 2.5 }}>
<Grid container spacing={3}>
<Grid item xs={12}>
{values.taskItems.length > 0 &&
values.taskItems.map((item, index) => {
return (
<>
<Box
sx={{
'& .MuiAccordion-root': {
borderColor: theme.palette.divider,
'& .MuiAccordionSummary-root': {
bgcolor: 'transparent',
flexDirection: 'row',
'&:focus-visible': {
bgcolor: 'primary.lighter'
}
},
'& .MuiAccordionDetails-root': {
borderColor: theme.palette.divider
},
'& .Mui-expanded': {
color: theme.palette.primary.main
}
}
}}
>
<Accordion expanded={expanded === `panel${index}`} onChange={handleChange(`panel${index}`)}>
<AccordionSummary aria-controls={`panel${index}d-content`} id={`panel${index}d-header`}>
<Stack direction="row" spacing={1.5} alignItems="center">
<DeleteFilled
onClick={() => { remove(index) }}
/>
<Typography variant="h6"> {values.taskItems[index].title ? values.taskItems[index].title : "N/A"}</Typography>
</Stack>
</AccordionSummary>
<AccordionDetails>
<Grid container spacing={2}>
<Grid item xs={12}>
<Stack spacing={1.25}>
<InputLabel htmlFor={`taskItems.${index}.title`}>Task Title</InputLabel>
<TextField
fullWidth
id={`taskItems.${index}.title`}
placeholder="Enter Task Title"
{...getFieldProps(`taskItems.${index}.title`)}
// error={Boolean(touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory)}
// helperText={touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory}
/>
</Stack>
</Grid>
<Grid item xs={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor={`taskItems.${index}.referenceImage`}>Task Reference Image</InputLabel>
<TextField
fullWidth
id={`taskItems.${index}.referenceImage`}
placeholder="Enter Task Reference Image"
{...getFieldProps(`taskItems.${index}.referenceImage`)}
// error={Boolean(touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory)}
// helperText={touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory}
/>
</Stack>
</Grid>
<Grid item xs={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor={`taskItems.${index}.referenceVideo`}>Task Reference Video</InputLabel>
<TextField
fullWidth
id={`taskItems.${index}.referenceVideo`}
placeholder="Enter Task Reference Video"
{...getFieldProps(`taskItems.${index}.referenceVideo`)}
// error={Boolean(touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory)}
// helperText={touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory}
/>
</Stack>
</Grid>
<Grid item xs={12}>
<Stack spacing={1.25}>
<InputLabel htmlFor={`taskItems.${index}.description`}>Task Description</InputLabel>
<TextField
fullWidth
id={`taskItems.${index}.description`}
placeholder="Enter Task Description"
{...getFieldProps(`taskItems.${index}.description`)}
// error={Boolean(touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory)}
// helperText={touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory}
/>
</Stack>
</Grid>
<Grid item xs={12}>
<Stack spacing={1.25}>
<InputLabel htmlFor={`taskItems.${index}.howToDo`}>Task How To Do</InputLabel>
<TextField
fullWidth
id={`taskItems.${index}.howToDo`}
placeholder="Enter Task How To Do"
{...getFieldProps(`taskItems.${index}.howToDo`)}
// error={Boolean(touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory)}
// helperText={touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory}
/>
</Stack>
</Grid>
</Grid>
</AccordionDetails>
</Accordion>
</Box>
</>
)
})
}
</Grid >
</Grid>
</Box>
</MainCard>
</>
)
}}
</FieldArray>
</Grid>
</Grid>
</Grid>
</Grid>
</DialogContent>
<Divider />
<DialogActions sx={{ p: 2.5 }}>
<Grid container justifyContent="space-between" alignItems="center">
<Grid item>
{!isCreating && (
<Tooltip title="Delete Tutorial" placement="top">
<IconButton onClick={() => setOpenAlert(true)} size="large" color="error">
<DeleteFilled />
</IconButton>
</Tooltip>
)}
</Grid>
<Grid item>
<Stack direction="row" spacing={2} alignItems="center">
<Button color="error" onClick={onCancel}>
Cancel
</Button>
<Button type="submit" variant="contained" disabled={isSubmitting}>
{tutorial ? 'Edit' : 'Add'}
</Button>
</Stack>
</Grid>
</Grid>
</DialogActions>
</Form>
</LocalizationProvider>
</FormikProvider>
{!isCreating && <AlertTutorialDelete title={""} open={openAlert} handleClose={handleAlertClose} deleteId={tutorial._id} />}
</>
);
};
export default AddEditTutorial;
......@@ -13,7 +13,7 @@ interface Props {
title: string;
open: boolean;
handleClose: (status: boolean) => void;
deleteId: number | null;
deleteId: number | string | undefined;
}
// ==============================|| Tutorial - DELETE ||============================== //
......
import axios from 'axios';
class SignLanguageToTextService {
predictSignLanguageVideo(speed, data) {
return axios.post(
`http://127.0.0.1:8000/predict-sign-language/video/speed_levels?speed=${speed}`,
data
);
}
}
export default new SignLanguageToTextService();
import { tutorialType } from "./tutorial"
export interface curriculumType {
_id?: string
curriculumCode: string
curriculumLevel: number
curriculumTitle: string
curriculumDescription: string
curriculumImage: string
curriculumMark: number
tutorials: tutorialType[],
status: number
createdBy: string
createdAt: Date
updatedBy?: string
updatedAt?: Date
}
\ No newline at end of file
export interface taskItemType {
_id?: string
title: string,
description: string,
howToDo: string[],
referenceImage: string,
referenceVideo: string,
taskItemMark: number
}
\ No newline at end of file
import { taskItemType } from "./taskItem"
export interface tutorialType {
_id?: string
tutorialCode?: string
tutorialTitle?: string
tutorialDescription?: string
tutorialImage?: string
tutorialMark: number
taskItems: taskItemType[]
status: number
createdBy: string
createdAt: Date
updatedBy?: string
updatedAt?: Date
}
\ No newline at end of file
export interface userProgressType {
_id: string
userId: string
curriculums?: curriculumTypeUserProgress[]
totalCurriculumsMarks: number
totalCurriculumSpentTime: number
status: number
createdBy?: string
createdAt: Date
updatedBy?: string
updatedAt?: Date
}
export interface curriculumTypeUserProgress {
curriculumCode: string
curriculumLevel: number
curriculumTitle: string
curriculumDescription: string
curriculumImage: string
curriculumMark: number
curriculumMarkUser: number
curriculumSpentTime: number
tutorials: tutorialTypeUserProgress[],
}
export interface tutorialTypeUserProgress {
tutorialCode?: string
tutorialTitle?: string
tutorialDescription?: string
tutorialImage?: string
tutorialMark: number
tutorialMarkUser: number
tutorialSpentTime: number
taskItems: taskItemTypeUserProgress[]
}
export interface taskItemTypeUserProgress {
title: string,
description: string,
howToDo: string[],
referenceImage: string,
referenceVideo: string,
taskItemMark: number
taskItemMarkUser: number
taskItemSpentTime: number
}
\ No newline at end of file
......@@ -162,7 +162,11 @@
"learning-lead-board": "Lead Board",
"learning-feedback": "Feedback",
"ssl-translate": "SSL Translate",
"emotion-detection": "Emotion Detection",
"audio-detection": "Audio Detection",
"video-detection": "Video Detection",
"learning-dashboard": "Dashboard",
"learning-curriculums-subscribed-tutorial": "Tutorial",
"video-to-sign-language": "Sign Language Translate",
"video-translate": "Video Translator",
"learning-dashboard": "Dashboard"
"video-translate": "Video Translator"
}
\ No newline at end of file
......@@ -42,11 +42,13 @@
"amazon-cognito-identity-js": "^5.2.11",
"apexcharts": "^3.36.0",
"autosuggest-highlight": "^3.3.4",
"axios": "^1.1.2",
"axios": "^1.4.0",
"change-case": "^4.1.2",
"date-fns": "^2.29.3",
"firebase": "^9.12.1",
"form-data": "^4.0.0",
"framer-motion": "^7.5.3",
"fs": "^0.0.1-security",
"highlight.js": "^11.6.0",
"i18next": "^21.10.0",
"i18next-browser-languagedetector": "^6.1.8",
......
......@@ -3,7 +3,7 @@ import { PATH_DASHBOARD } from './routes/paths';
// API
// ----------------------------------------------------------------------
export const BACKEND_URL = 'http://127.0.0.1:8000/';
export const HOST_API_KEY = process.env.HOST_API_KEY || '';
export const FIREBASE_API = {
......
......@@ -20,6 +20,7 @@ const LINKS = [
{ name: 'About us', href: PATH_PAGE.about },
{ name: 'Contact us', href: PATH_PAGE.contact },
{ name: 'FAQs', href: PATH_PAGE.faqs },
{ name: 'Emotion Detection', href: PATH_PAGE.emotion },
],
},
{
......
......@@ -33,6 +33,7 @@ const navConfig = [
{ title: 'Payment', path: PATH_PAGE.payment },
{ title: 'Maintenance', path: PATH_PAGE.maintenance },
{ title: 'Coming Soon', path: PATH_PAGE.comingSoon },
{ title: 'Emotion Detection', path: PATH_PAGE.emotion },
],
},
{
......
// next
import Head from 'next/head';
// @mui
import { Container, Box } from '@mui/material';
// layouts
import MainLayout from '../layouts/main';
// _mock
// import { _mapEmotion } from '../_mock/arrays';
// sections
import { EmotionHero, EmotionForm, } from '../sections/emotion';
// ----------------------------------------------------------------------
EmotionDetection.getLayout = (page: React.ReactElement) => <MainLayout>{page}</MainLayout>;
// ----------------------------------------------------------------------
export default function EmotionDetection() {
return (
<>
<Head>
<title> Emotion Detection </title>
</Head>
<EmotionHero />
<Container sx={{ py: 10 }}>
<Box
gap={10}
display="grid"
gridTemplateColumns={{
xs: 'repeat(1, 1fr)',
md: 'repeat(2, 1fr)',
}}
>
<EmotionForm />
{/* <EmotionMap emotions={_mapEmotion} /> */}
</Box>
</Container>
</>
);
}
......@@ -21,6 +21,7 @@ import {
Paper,
CircularProgress,
LinearProgress,
Slider,
} from '@mui/material';
// layouts
import MainLayout from '../layouts/main';
......@@ -41,6 +42,9 @@ import { useSnackbar } from 'notistack';
import useCopyToClipboard from 'src/hooks/useCopyToClipboard';
import Iconify from 'src/components/iconify/Iconify';
import dynamic from 'next/dynamic';
import SignLanguageToTextService from 'src/services/SignLanguageToText.js';
import { Block } from 'src/sections/_examples/Block';
const useReactMediaRecorder = () =>
// eslint-disable-next-line react-hooks/rules-of-hooks
......@@ -57,7 +61,9 @@ export default function AboutPage() {
const [isUploadFile, setIsUploadFile] = useState<boolean | string | null>(true);
const [videoUrl, setVideoUrl] = useState('');
const [loading, setLoading] = useState(false);
const [value, setValue] = useState('ආආආආආආආආආආආආආආආආ');
const [value, setValue] = useState('');
const [speed, setSpeed] = useState(0);
const [recordedVideoUrl, setRecordedVideoUrl] = useState(null);
const handleDropSingleFile = useCallback(async (acceptedFiles: File[]) => {
const file = acceptedFiles[0];
......@@ -105,6 +111,68 @@ export default function AboutPage() {
// Video Upload
const translateSignLanguageToText = async () => {
if (file) {
setLoading(true);
const formData = new FormData();
formData.append('video_request', file, file.name);
try {
const response = await SignLanguageToTextService.predictSignLanguageVideo(speed, formData);
if (response.status == 200) {
console.log(response.data);
setValue(response.data.predictions);
} else {
enqueueSnackbar('Something went Wrong!', { variant: 'error' });
}
setLoading(false);
} catch (error) {
console.log(error);
setLoading(false);
enqueueSnackbar('Something went Wrong!', { variant: 'error' });
}
} else {
enqueueSnackbar('Please select a file.', { variant: 'warning' });
}
};
const translateSignLanguageToTextRecord = async () => {
console.log('TEST TES ');
console.log(recordedVideoUrl);
if (recordedVideoUrl) {
setLoading(true);
const formData = new FormData();
formData.append('video_request', recordedVideoUrl, recordedVideoUrl.name);
try {
const response = await SignLanguageToTextService.predictSignLanguageVideo(speed, formData);
if (response.status == 200) {
console.log(response.data);
setValue(response.data.predictions);
} else {
enqueueSnackbar('Something went Wrong!', { variant: 'error' });
}
setLoading(false);
} catch (error) {
console.log(error);
setLoading(false);
enqueueSnackbar('Something went Wrong!', { variant: 'error' });
}
} else {
enqueueSnackbar('Please record a video.', { variant: 'warning' });
}
};
function valuetext(value: number) {
setSpeed(value);
return `$${value}°C`;
}
const handleVideoRecorded = (url) => {
setRecordedVideoUrl(url);
};
return (
<>
<Head>
......@@ -144,6 +212,7 @@ export default function AboutPage() {
file={file}
onDrop={handleDropSingleFile}
onDelete={() => setFile(null)}
multiple={true}
/>
{file && (
......@@ -163,6 +232,45 @@ export default function AboutPage() {
<Card sx={{ p: 5, minHeight: 300 }}>
<Box display="grid" gap={5}>
<Stack spacing={2}>
<Grid container spacing={2}>
<Grid item xs={12} md={6}>
<h3>Set Sign Speed </h3>
<Slider
defaultValue={30}
getAriaValueText={valuetext}
valueLabelDisplay="auto"
step={10}
marks
min={10}
max={110}
/>
<h4>Speed - {speed}</h4>
</Grid>
<Grid
item
xs={12}
md={6}
container
direction="row"
justifyContent="flex-end"
alignItems="center"
>
<Button
variant="contained"
style={{ width: '200px', height: '60px', fontSize: '20px' }}
sx={{
mb: 3,
}}
disabled={loading}
onClick={() => {
translateSignLanguageToText();
}}
>
Translate
</Button>
</Grid>
</Grid>
{loading ? (
<Card>
<CardContent>
......@@ -202,20 +310,6 @@ export default function AboutPage() {
</Box>
</Card>
</Grid>
<Grid item xs={12} md={12}>
<center>
<Button
variant="contained"
style={{ width: '200px', height: '60px', fontSize: '20px' }}
sx={{
mb: 3,
}}
disabled={loading}
>
Translate
</Button>
</center>
</Grid>
</Grid>
</Card>
</Box>
......@@ -224,19 +318,65 @@ export default function AboutPage() {
<Box sx={{ flexGrow: 1 }}>
<Card>
<CardHeader title="Upload a video containing Sign Language" />
<CardHeader title="Capture a video containing Sign Language" />
<Grid container spacing={2}>
<Grid item xs={12} md={6}>
<Card>
<CardContent>
<WebcamStreamCapture />
<WebcamStreamCapture onVideoRecorded={handleVideoRecorded} />
</CardContent>
{recordedVideoUrl && (
<div>
<h2>Recorded Video</h2>
<video src={recordedVideoUrl} controls autoPlay />
</div>
)}
</Card>
</Grid>
<Grid item xs={12} md={6}>
<Card sx={{ p: 5, minHeight: 300 }}>
<Box display="grid" gap={5}>
<Stack spacing={2}>
<Grid container spacing={2}>
<Grid item xs={12} md={6}>
<h3>Set Sign Speed </h3>
<Slider
defaultValue={30}
getAriaValueText={valuetext}
valueLabelDisplay="auto"
step={10}
marks
min={10}
max={110}
/>
<h4>Speed - {speed}</h4>
</Grid>
<Grid
item
xs={12}
md={6}
container
direction="row"
justifyContent="flex-end"
alignItems="center"
>
<Button
variant="contained"
style={{ width: '200px', height: '60px', fontSize: '20px' }}
sx={{
mb: 3,
}}
disabled={loading}
onClick={() => {
translateSignLanguageToTextRecord();
}}
>
Translate
</Button>
</Grid>
</Grid>
{loading ? (
<Card>
<CardContent>
......@@ -276,20 +416,6 @@ export default function AboutPage() {
</Box>
</Card>
</Grid>
<Grid item xs={12} md={12}>
<center>
<Button
variant="contained"
style={{ width: '200px', height: '60px', fontSize: '20px' }}
sx={{
mb: 3,
}}
disabled={loading}
>
Translate
</Button>
</center>
</Grid>
</Grid>
</Card>
</Box>
......
......@@ -27,6 +27,7 @@ export const PATH_PAGE = {
payment: '/payment',
about: '/about-us',
contact: '/contact-us',
emotion : '/emotion-detection',
faqs: '/faqs',
page403: '/403',
page404: '/404',
......
import { m } from 'framer-motion';
// @mui
import { Button, Typography, TextField, Stack } from '@mui/material';
// components
import { MotionViewport, varFade } from '../../components/animate';
// ----------------------------------------------------------------------
export default function EmotionForm() {
return (
<Stack component={MotionViewport} spacing={5}>
<m.div variants={varFade().inUp}>
<Typography variant="h3">
Feel free to contact us. <br />
We'll be glad to hear from you, buddy.
</Typography>
</m.div>
<Stack spacing={3}>
<m.div variants={varFade().inUp}>
<TextField fullWidth label="Name" />
</m.div>
<m.div variants={varFade().inUp}>
<TextField fullWidth label="Email" />
</m.div>
<m.div variants={varFade().inUp}>
<TextField fullWidth label="Subject" />
</m.div>
<m.div variants={varFade().inUp}>
<TextField fullWidth label="Enter your message here." multiline rows={4} />
</m.div>
</Stack>
<m.div variants={varFade().inUp}>
<Button size="large" variant="contained">
Submit Now
</Button>
</m.div>
</Stack>
);
}
import { m } from 'framer-motion';
// @mui
import { styled } from '@mui/material/styles';
import { Stack, Container, Typography, Grid } from '@mui/material';
//
import { TextAnimate, MotionContainer, varFade } from '../../components/animate';
// ----------------------------------------------------------------------
const EMOTIONS = [
{
country: 'Bali',
address: '508 Bridle Avenue Newnan, GA 30263',
phoneNumber: '(239) 555-0108',
},
{
country: 'London',
address: '508 Bridle Avenue Newnan, GA 30263',
phoneNumber: '(319) 555-0115',
},
{
country: 'Prague',
address: '508 Bridle Avenue Newnan, GA 30263',
phoneNumber: '(252) 555-0126',
},
{
country: 'Moscow',
address: '508 Bridle Avenue Newnan, GA 30263',
phoneNumber: '(307) 555-0133',
},
];
const StyledRoot = styled('div')(({ theme }) => ({
position: 'relative',
backgroundSize: 'cover',
backgroundPosition: 'center',
backgroundImage: 'url(/assets/background/overlay_1.svg), url(/assets/images/contact/hero.jpg)',
padding: theme.spacing(10, 0),
[theme.breakpoints.up('md')]: {
height: 560,
padding: 0,
},
}));
const StyledContent = styled('div')(({ theme }) => ({
textAlign: 'center',
[theme.breakpoints.up('md')]: {
bottom: 80,
textAlign: 'left',
position: 'absolute',
},
}));
// ----------------------------------------------------------------------
export default function EmotionHero() {
return (
<StyledRoot>
<Container component={MotionContainer}>
<StyledContent>
<TextAnimate text="Emotion" sx={{ color: 'primary.main' }} variants={varFade().inRight} />
<br />
<Stack spacing={2} display="inline-flex" direction="row" sx={{ color: 'common.white' }}>
<TextAnimate text="Detection" />
<TextAnimate text="find" />
<TextAnimate text="us?" />
</Stack>
<Grid container spacing={5} sx={{ mt: 5, color: 'common.white' }}>
{EMOTIONS.map((emotion) => (
<Grid
key={emotion.country}
item
xs={12}
sm={6}
md={3}
lg={2}
sx={{
pr: {
md: 5,
},
}}
>
<m.div variants={varFade().in}>
<Typography variant="h6" paragraph>
{emotion.country}
</Typography>
</m.div>
<m.div variants={varFade().inRight}>
<Typography variant="body2">
{emotion.address}
<br /> {emotion.phoneNumber}
</Typography>
</m.div>
</Grid>
))}
</Grid>
</StyledContent>
</Container>
</StyledRoot>
);
}
// export { default as EmotionMap } from './EmotionMap';
export { default as EmotionHero } from './EmotionHero';
export { default as EmotionForm } from './EmotionForm';
import React, { useEffect, useState } from 'react';
import Webcam from 'react-webcam';
import { Box, Button, Container, Grid, Stack } from '@mui/material';
import React, { useEffect, useRef, useState } from 'react';
import { Button, Grid } from '@mui/material';
import StopIcon from '@mui/icons-material/Stop';
import RadioButtonCheckedIcon from '@mui/icons-material/RadioButtonChecked';
import styles from './WebcamStreamCapture.module.css';
const WebcamStreamCapture = () => {
const webcamRef = React.useRef(null);
const mediaRecorderRef = React.useRef(null);
const [capturing, setCapturing] = React.useState(false);
const [recordedChunks, setRecordedChunks] = React.useState([]);
const [mediaBlobUrl, setMediaBlobUrl] = React.useState([]);
import Webcam from 'react-webcam';
const handleStartCaptureClick = React.useCallback(() => {
const WebcamStreamCapture = ({ onVideoRecorded }) => {
const webcamRef = useRef(null);
const mediaRecorderRef = useRef(null);
const [capturing, setCapturing] = useState(false);
const [recordedChunks, setRecordedChunks] = useState([]);
const handleStartCaptureClick = () => {
setRecordedChunks([]);
setMediaBlobUrl([]);
setCapturing(true);
mediaRecorderRef.current = new MediaRecorder(webcamRef.current.stream, {
......@@ -21,112 +19,81 @@ const WebcamStreamCapture = () => {
});
mediaRecorderRef.current.addEventListener('dataavailable', handleDataAvailable);
mediaRecorderRef.current.start();
}, [webcamRef, setCapturing, mediaRecorderRef]);
};
const handleDataAvailable = React.useCallback(
({ data }) => {
if (data.size > 0) {
setRecordedChunks((prev) => prev.concat(data));
}
},
[setRecordedChunks]
);
const handleDataAvailable = ({ data }) => {
if (data.size > 0) {
setRecordedChunks((prev) => prev.concat(data));
}
};
const handleStopCaptureClick = React.useCallback(async () => {
const handleStopCaptureClick = () => {
mediaRecorderRef.current.stop();
const blob = new Blob(recordedChunks, {
type: 'video/mp4',
});
const url = await URL.createObjectURL(blob);
console.log(url);
await setMediaBlobUrl(url);
setCapturing(false);
}, [mediaRecorderRef, webcamRef, setCapturing]);
};
const handleDownload = React.useCallback(() => {
const handleDownload = () => {
if (recordedChunks.length) {
const blob = new Blob(recordedChunks, {
type: 'video/mp4',
type: 'video/webm', // Use 'video/webm' to match MediaRecorder mimeType
});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
document.body.appendChild(a);
a.style = 'display: none';
a.href = url;
a.download = 'user-recording.mp4';
a.click();
window.URL.revokeObjectURL(url);
onVideoRecorded(url); // Pass the blob URL to the parent component
setRecordedChunks([]);
}
}, [recordedChunks]);
// Styles for camera
const [width, setWidth] = useState(window.innerWidth);
const handleResize = () => {
setWidth(window.innerWidth);
};
useEffect(() => {
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return (
<>
<Grid container spacing={2}>
<Grid item xs={12}>
<center>
<Webcam audio={false} ref={webcamRef} style={{ width: '100%', maxWidth: '500px' }} />
</center>
</Grid>
<Grid item xs={12}>
<center>
{capturing ? (
<Button
onClick={handleStopCaptureClick}
startIcon={<StopIcon />}
color="error"
variant="contained"
>
Stop Capture
</Button>
) : (
<Button
onClick={handleStartCaptureClick}
startIcon={<RadioButtonCheckedIcon />}
color="error"
variant="contained"
>
Start Capture
</Button>
)}
{recordedChunks.length > 0 && (
<Button
onClick={handleDownload}
variant="contained"
sx={{
ml: 1,
}}
>
Download
</Button>
)}
</center>
</Grid>
<Grid item xs={12}>
<Grid container spacing={2}>
<Grid item xs={12}>
<center>
<Webcam audio={false} ref={webcamRef} style={{ width: '100%', maxWidth: '500px' }} />
</center>
</Grid>
<Grid item xs={12}>
<center>
{capturing ? (
<Button
onClick={handleStopCaptureClick}
startIcon={<StopIcon />}
color="error"
variant="contained"
>
Stop Capture
</Button>
) : (
<Button
onClick={handleStartCaptureClick}
startIcon={<RadioButtonCheckedIcon />}
color="error"
variant="contained"
>
Start Capture
</Button>
)}
{recordedChunks.length > 0 && (
<center>
<video src={mediaBlobUrl} controls autoPlay />
</center>
<Button onClick={handleDownload} variant="contained" sx={{ ml: 1 }}>
Download
</Button>
)}
</Grid>
</center>
</Grid>
<Grid item xs={12}>
{recordedChunks.length > 0 && (
<center>
<video
src={
recordedChunks.length > 0
? URL.createObjectURL(new Blob(recordedChunks, { type: 'video/webm' }))
: null
}
controls
autoPlay
/>
</center>
)}
</Grid>
</>
</Grid>
);
};
......
import axios from 'axios';
class SignLanguageToTextService {
predictSignLanguageVideo(speed, data) {
return axios.post(
`http://127.0.0.1:8000/predict-sign-language/video/speed_levels?speed=${speed}`,
data
);
}
}
export default new SignLanguageToTextService();
# Project ID: 2023-029
### test
# Project Name: “Sign Language Translation with emotional base multi model,3D Avatar and Adaptive Learning.”
## Main Objective
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment