Skip to content

Commit

Permalink
Merge pull request #42 from SLIIT-24-25J-047-Research/Development
Browse files Browse the repository at this point in the history
Development
  • Loading branch information
BoshithaMGunarathna authored Nov 29, 2024
2 parents db03f38 + 8d9468b commit f64c27d
Show file tree
Hide file tree
Showing 25 changed files with 599 additions and 85 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
frontend/.env
backend/.env
backend/uploads
backend/candidate_classification_service/model/final_trained_model.pth
backend/node_modules
frontend/node_modules
21 changes: 21 additions & 0 deletions backend/candidate_classification_service/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Ignore Python virtual environment
venv/
# Ignore Python cache files
*.pyc
*.pyo
*.pyd
*.py[cod]
__pycache__/

# Ignore Jupyter Notebook checkpoints
.ipynb_checkpoints/

# Ignore IDE specific files
.idea/
.vscode/

# Ignore local configuration files (optional)
.env
pyvenv.cfg

models
28 changes: 28 additions & 0 deletions backend/candidate_classification_service/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# prebuilt base image with large dependencies
FROM python-base:latest

# Working directory
WORKDIR /app

# Install system dependencies for PyTorch
RUN apt-get update && apt-get install -y \
libgl1-mesa-glx \
&& rm -rf /var/lib/apt/lists/*

# Create the uploads directory
RUN mkdir -p /app/uploads

# Copy only the requirements.txt to leverage Docker caching
COPY requirements.txt .

# Install Python dependencies
RUN pip install --no-cache-dir -r requirements.txt

# Copy the entire application code
COPY . .

# Expose the application port
EXPOSE 3003

# Command to run the Flask app
CMD ["python", "app.py"]
76 changes: 76 additions & 0 deletions backend/candidate_classification_service/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import os
import torch
from flask import Flask, request, jsonify
from flask_cors import CORS
from PIL import Image
from torchvision import models, transforms
import io
import requests # To send the result to Node.js

# Initialize Flask app
app = Flask(__name__)
CORS(app)

# Define device and load the model
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# Load the trained model
model_path = './model/final_trained_model.pth'
model = models.resnet18(weights=None) # My model is initiating to learning architecture from this ResNet18 architecture
model.fc = torch.nn.Linear(model.fc.in_features, 4) # Adjust for 4 classes
model.load_state_dict(torch.load(model_path, map_location=device, weights_only=True))
model = model.to(device)
model.eval()

# Define class names
class_names = ["clean_casual", "clean_formal", "messy_casual", "messy_formal"]

# Define image transformations (same as in training)
transform = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])

# Prediction function
def predict_image(image_bytes):
image = Image.open(io.BytesIO(image_bytes)) # Load image from bytes
image = transform(image).unsqueeze(0).to(device) # Apply transformation and add batch dimension
with torch.no_grad():
outputs = model(image)
_, predicted = torch.max(outputs, 1) # Get the predicted class
return class_names[predicted.item()]

# Create route for prediction
@app.route('/classification-predict', methods=['POST'])
def predict():
if 'file' not in request.files:
return jsonify({"error": "No file part"}), 400
file = request.files['file']
if file.filename == '':
return jsonify({"error": "No selected file"}), 400

try:
image_bytes = file.read() # Read the file content as bytes
prediction = predict_image(image_bytes) # Get prediction

# Send the result to the Node.js backend (assuming API expects prediction and email)
user_email = request.form.get("email")
if user_email:
response = requests.post(
'http://localhost:5000/api/savePrediction', # Adjust the URL to your Node.js server
json={'prediction': prediction, 'email': user_email}
)
if response.status_code == 200:
return jsonify({"prediction": prediction, "status": "Prediction saved"})
else:
return jsonify({"error": "Failed to save prediction in database"}), 500
else:
return jsonify({"error": "Email is required"}), 400

except Exception as e:
return jsonify({"error": str(e)}), 500

# Run the app
if __name__ == '__main__':
app.run(host='0.0.0.0', port=3003, debug=False)
18 changes: 18 additions & 0 deletions backend/candidate_classification_service/base/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Use the slim Python image as the base
FROM python:3.12-slim

# Set the working directory
WORKDIR /app

# Install necessary system packages
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
libjpeg-dev \
zlib1g-dev \
&& rm -rf /var/lib/apt/lists/*

# Install Python dependencies that are common for the service
RUN pip install --no-cache-dir \
torch \
torchvision \
Pillow
4 changes: 4 additions & 0 deletions backend/candidate_classification_service/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
torch
torchvision
flask
pillow
82 changes: 56 additions & 26 deletions backend/controllers/authController.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const User = require('../models/User');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const User = require("../models/User");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");

// Register User
const register = async (req, res) => {
Expand All @@ -10,15 +10,15 @@ const register = async (req, res) => {
// Check if user already exists
const existingUser = await User.findOne({ email });
if (existingUser) {
return res.status(400).json({ message: 'User already exists' });
return res.status(400).json({ message: "User already exists" });
}

// Create new user (hashing is done automatically in the User schema)
const newUser = new User({
email,
password, // No need to hash here, it's handled in the schema
role,
name
name,
});

await newUser.save();
Expand All @@ -27,65 +27,95 @@ const register = async (req, res) => {
const token = jwt.sign(
{ id: newUser._id, role: newUser.role },
process.env.JWT_SECRET,
{ expiresIn: '1h' }
{ expiresIn: "1h" }
);

// Return the token and user role
res.status(201).json({
token,
role: newUser.role
role: newUser.role,
});

} catch (error) {
console.error('Error during registration:', error);
res.status(500).json({ message: 'Server error' });
console.error("Error during registration:", error);
res.status(500).json({ message: "Server error" });
}
};


// Login User
const login = async (req, res) => {
const { email, password } = req.body;

try {
console.log('Checking for user with email:', email);
console.log("Checking for user with email:", email);

// Check if user exists
const user = await User.findOne({ email });
if (!user) {
console.log('User not found');
return res.status(404).json({ message: 'User not found' }); // Added return to stop further execution
console.log("User not found");
return res.status(404).json({ message: "User not found" }); // Return response and stop further execution
}

console.log('User found:');
console.log("User found:");

// Check if password matches
const isMatch = await user.comparePassword(password);
if (!isMatch) {
console.log('Password mismatch');
return res.status(400).json({ message: 'Invalid credentials' }); // Added return to stop further execution
console.log("Password mismatch");
return res.status(400).json({ message: "Invalid credentials" }); // Return response and stop further execution
}

console.log('Password match, generating token');
console.log("Password match, generating token");

// Generate JWT token
const token = jwt.sign(
{ id: user._id, role: user.role },
{ id: user._id, role: user.role, email: user.email }, // Include email in JWT payload
process.env.JWT_SECRET,
{ expiresIn: '1h' }
{ expiresIn: "1h" } // Token expiration time
);

// Send back the token and user role
// Send back the token, user role, and email
return res.json({
token,
role: user.role
}); // Ensure this is the last response
role: user.role,
email: user.email, // Include email in the response
});
} catch (error) {
console.error("Error during login:", error);
return res.status(500).json({ message: "Server error" }); // Return response and stop further execution
}
};


// Get logged-in user data based on email query
const getUserData = async (req, res) => {
const { email } = req.query; // Retrieve the email from query parameters

try {
// Validate if email exists
if (!email) {
return res.status(400).json({ message: "Email is required" });
}

// Find user by email
const user = await User.findOne({ email }).select("-password"); // Exclude the password field
if (!user) {
return res.status(404).json({ message: "User not found" });
}

// Return user data
res.json({
id: user._id,
name: user.name,
email: user.email,
role: user.role,
// Add any other fields you want to return
});
} catch (error) {
console.error('Error during login:', error);
return res.status(500).json({ message: 'Server error' }); // Added return to stop further execution
console.error("Error fetching user data:", error);
res.status(500).json({ message: "Server error" });
}
};

//---

module.exports = { login , register };
module.exports = { login, register, getUserData };
21 changes: 21 additions & 0 deletions backend/controllers/candidate/classificationController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const ImageClassification = require('../../models/candidate/imageClassification');

// Function to store classification result
const storePrediction = async (email, prediction) => {
try {
const newClassification = new ImageClassification({
email: email,
prediction: prediction,
});
await newClassification.save();
return { success: true, message: 'Prediction saved successfully' };
} catch (error) {
console.error('Error saving prediction:', error);
return { success: false, message: 'Error saving prediction' };
}
};


module.exports = {
storePrediction,
};
18 changes: 18 additions & 0 deletions backend/controllers/candidate/predictionController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const Prediction = require("../../models/candidate/Prediction");

exports.savePrediction = async (req, res) => {
const { email, prediction } = req.body;

if (!email || !prediction) {
return res.status(400).json({ error: "Email and prediction are required" });
}

try {
const newPrediction = new Prediction({ email, prediction });
await newPrediction.save();
return res.status(200).json({ message: "Prediction saved successfully" });
} catch (error) {
console.error(error);
return res.status(500).json({ error: "Failed to save prediction" });
}
};
27 changes: 18 additions & 9 deletions backend/controllers/predictController.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs');
const path = require('path');
const ffmpeg = require('fluent-ffmpeg');
const ffmpeg = require('fluent-ffmpeg'); // Import fluent-ffmpeg
const Prediction = require('../models/candidate/Prediction');

exports.predictConfidence = async (req, res) => {
try {
Expand Down Expand Up @@ -48,17 +49,25 @@ exports.predictConfidence = async (req, res) => {

console.log('Flask response:', flaskResponse.data); // Log Flask response

// fs.unlink(tempFilePath, (err) => {
// if (err) {
// console.error(`Error deleting converted file: ${err}`);
// } else {
// console.log(`Successfully deleted file: ${tempFilePath}`);
// }
// });
// Save prediction in the database
const newPrediction = new Prediction({
email: req.body.email,
prediction: flaskResponse.data.prediction
});
await newPrediction.save();

// Clean up the temporary converted audio file
fs.unlink(tempFilePath, (err) => {
if (err) {
console.error(`Error deleting converted file: ${err}`);
} else {
console.log(`Successfully deleted file: ${tempFilePath}`);
}
});

return res.status(200).json({
success: true,
prediction: flaskResponse.data
prediction: flaskResponse.data.prediction
});
} catch (error) {
console.error('Error in Flask response:', error.message);
Expand Down
Loading

0 comments on commit f64c27d

Please sign in to comment.