diff --git a/MisplaceAI/MisplaceAI/__pycache__/settings.cpython-310.pyc b/MisplaceAI/MisplaceAI/__pycache__/settings.cpython-310.pyc index 8217cae40ee3843e77dc74817b9b954ad653e373..e960bd634688fe21b9c521f53e0f352cc4d3ddb8 100644 Binary files a/MisplaceAI/MisplaceAI/__pycache__/settings.cpython-310.pyc and b/MisplaceAI/MisplaceAI/__pycache__/settings.cpython-310.pyc differ diff --git a/MisplaceAI/MisplaceAI/__pycache__/settings.cpython-39.pyc b/MisplaceAI/MisplaceAI/__pycache__/settings.cpython-39.pyc index 14ca3d9fd2ec57dde6a87341fbb00d4500a3edf3..31df9d414c9434dccb4753594eba6ed710b7b5d4 100644 Binary files a/MisplaceAI/MisplaceAI/__pycache__/settings.cpython-39.pyc and b/MisplaceAI/MisplaceAI/__pycache__/settings.cpython-39.pyc differ diff --git a/MisplaceAI/MisplaceAI/__pycache__/urls.cpython-310.pyc b/MisplaceAI/MisplaceAI/__pycache__/urls.cpython-310.pyc index 8483a1b21baf23ab9ef05a13e9bf9e1254ecb39f..a1664ddfcf212cf1b6857548f07d35419a15896b 100644 Binary files a/MisplaceAI/MisplaceAI/__pycache__/urls.cpython-310.pyc and b/MisplaceAI/MisplaceAI/__pycache__/urls.cpython-310.pyc differ diff --git a/MisplaceAI/MisplaceAI/settings.py b/MisplaceAI/MisplaceAI/settings.py index 001bfb42ba867edea794ac8a092fc53cb895a225..a1a77a2b06e3cef1d7837e6e993f4a828b7ceec7 100644 --- a/MisplaceAI/MisplaceAI/settings.py +++ b/MisplaceAI/MisplaceAI/settings.py @@ -1,4 +1,5 @@ """ + MisplaceAi/MisplaceAI/settings.py Django settings for MisplaceAI project. Generated by 'django-admin startproject' using Django 4.0.2. @@ -51,7 +52,7 @@ INSTALLED_APPS = [ 'process_misplaced_manager.apps.ProcessMisplacedManagerConfig', 'rest_framework', 'corsheaders', - + 'user_dashboard', 'rest_framework_simplejwt', ] diff --git a/MisplaceAI/MisplaceAI/urls.py b/MisplaceAI/MisplaceAI/urls.py index c1e4986b9219382381f473935b895129cccda8a0..a74789831728d5c059f465920366830dde8b1b0c 100644 --- a/MisplaceAI/MisplaceAI/urls.py +++ b/MisplaceAI/MisplaceAI/urls.py @@ -14,6 +14,8 @@ urlpatterns = [ path('api/process_misplaced_manager/', include('process_misplaced_manager.urls', namespace='process_misplaced_manager')), path('api/results_viewer/', include('results_viewer.urls', namespace='results_viewer')), path('api/placement_rules/', include('placement_rules.urls')), + path('api/user_dashboard/', include('user_dashboard.urls')), + ] if settings.DEBUG: diff --git a/MisplaceAI/placement_rules/utils.py b/MisplaceAI/placement_rules/utils.py index e04857c80a91df8a5f3806a6311168ec7a8e2722..9bc52a1023260a40dd37313fa4b0d9fce52aad66 100644 --- a/MisplaceAI/placement_rules/utils.py +++ b/MisplaceAI/placement_rules/utils.py @@ -4,64 +4,109 @@ from rules.models import Rule class PlacementRules: def __init__(self): + # Initialize the PlacementRules object and load rules from the database self.rules = self.load_rules_from_db() def load_rules_from_db(self): + """Load rules from the database and store them in a dictionary.""" rules_dict = {} + # Retrieve all Rule objects from the database rules = Rule.objects.all() for rule in rules: + # Extract the name of the item associated with the rule item_name = rule.item.name + # Extract the list of allowed locations for this item allowed_locations = list(rule.locations.values_list('name', flat=True)) + # Store the item name and its allowed locations in the dictionary rules_dict[item_name] = allowed_locations return rules_dict def check_placement(self, dataLocation): - """Check the placement of each object based on the rules.""" + """Check the placement of each object based on the rules. + + Args: + dataLocation (list): A list of dictionaries, each representing an object's data. + + Returns: + list: A list of misplaced objects with their allowed locations. + """ misplaced_objects = [] print("-----------------------------------") print("--CHECK-PLACEMENT-FOR-EACH-OBJECT--") + + # Iterate over each object in the dataLocation list for obj in dataLocation: object_name = obj["class_name"] + # Retrieve the allowed locations for the current object from the rules dictionary allowed_locations = self.rules.get(object_name, []) is_placed_correctly = False print(f"Checking {object_name}, allowed on: {allowed_locations}") + # Check if the object is correctly placed in any of the allowed locations for location in allowed_locations: is_on_top, _ = self.is_on_top_of(dataLocation, object_name, location) if is_on_top: is_placed_correctly = True break + # If the object is not placed correctly and there are allowed locations, it is misplaced if not is_placed_correctly and allowed_locations: - print( - f"{object_name} is misplaced. It should be on {', '.join(allowed_locations)}." - ) - obj["allowed_locations"] = allowed_locations # Add allowed locations to obj + print(f"{object_name} is misplaced. It should be on {', '.join(allowed_locations)}.") + # Add the allowed locations to the object for reference + obj["allowed_locations"] = allowed_locations + # Add the misplaced object to the list misplaced_objects.append(obj) print("-----------------------------------") return misplaced_objects def is_on_top_of(self, dataLocation, top_object_name, bottom_object_name): - # Filter objects by class_name - top_objects = [ - obj for obj in dataLocation if obj["class_name"] == top_object_name - ] - bottom_objects = [ - obj for obj in dataLocation if obj["class_name"] == bottom_object_name - ] + """Check if any object of top_object_name is on top of any object of bottom_object_name. + + Args: + dataLocation (list): A list of dictionaries, each representing an object's data. + top_object_name (str): The class name of the object to be checked if on top. + bottom_object_name (str): The class name of the object to be checked if below. + + Returns: + tuple: A boolean indicating if the top object is on top of the bottom object, and a message. + """ + # Filter objects by their class name + top_objects = [obj for obj in dataLocation if obj["class_name"] == top_object_name] + bottom_objects = [obj for obj in dataLocation if obj["class_name"] == bottom_object_name] # Define a function to check if two objects have horizontal overlap def has_horizontal_overlap(a, b): + """ + Check if two objects have horizontal overlap based on their bounding box coordinates. + + Args: + a (dict): Dictionary containing bounding box coordinates of the first object. + b (dict): Dictionary containing bounding box coordinates of the second object. + + Returns: + bool: True if there is horizontal overlap, False otherwise. + """ + # 'xmax' is the right edge and 'xmin' is the left edge of the bounding box. + # Check if there is no horizontal overlap: + # 1. a["xmax"] <= b["xmin"]: True if the right edge of 'a' is to the left of or exactly at the left edge of 'b' + # 2. a["xmin"] >= b["xmax"]: True if the left edge of 'a' is to the right of or exactly at the right edge of 'b' + # If either of the above conditions is true, there is no overlap. + # 'not' inverts this to check for overlap instead. return not (a["xmax"] <= b["xmin"] or a["xmin"] >= b["xmax"]) - # Check if any of the top objects is on top of any of the bottom objects + + # Check if any top object is on top of any bottom object for top in top_objects: for bottom in bottom_objects: + # Check if the top and bottom objects have horizontal overlap if has_horizontal_overlap(top, bottom): + # Check if the top object's ymax is within the y-range of the bottom object if top["ymax"] <= bottom["ymax"] and top["ymax"] >= bottom["ymin"]: + # Return True if the top object is found on top of the bottom object return ( True, f"Object {top['Object']} ({top['class_name']}) is on top of Object {bottom['Object']} ({bottom['class_name']})", ) + # Return False if no top object is found on top of a bottom object return False, f"No {top_object_name} is on top of any {bottom_object_name}." diff --git a/MisplaceAI/user_dashboard/__init__.py b/MisplaceAI/user_dashboard/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/MisplaceAI/user_dashboard/admin.py b/MisplaceAI/user_dashboard/admin.py new file mode 100644 index 0000000000000000000000000000000000000000..8c38f3f3dad51e4585f3984282c2a4bec5349c1e --- /dev/null +++ b/MisplaceAI/user_dashboard/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/MisplaceAI/user_dashboard/apps.py b/MisplaceAI/user_dashboard/apps.py new file mode 100644 index 0000000000000000000000000000000000000000..4b36bdd587302592befc82720c4e0be7c397074f --- /dev/null +++ b/MisplaceAI/user_dashboard/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class UserDashboardConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'user_dashboard' diff --git a/MisplaceAI/user_dashboard/models.py b/MisplaceAI/user_dashboard/models.py new file mode 100644 index 0000000000000000000000000000000000000000..71a836239075aa6e6e4ecb700e9c42c95c022d91 --- /dev/null +++ b/MisplaceAI/user_dashboard/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/MisplaceAI/user_dashboard/serializers.py b/MisplaceAI/user_dashboard/serializers.py new file mode 100644 index 0000000000000000000000000000000000000000..46b3e496d9d9d34bd7a2189cdee157b13ad98a4b --- /dev/null +++ b/MisplaceAI/user_dashboard/serializers.py @@ -0,0 +1,8 @@ +# MisplaceAI/user_dashboard/serializers.py +from django.contrib.auth.models import User +from rest_framework import serializers + +class UserSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = ['username', 'email', 'first_name', 'last_name'] diff --git a/MisplaceAI/user_dashboard/tests.py b/MisplaceAI/user_dashboard/tests.py new file mode 100644 index 0000000000000000000000000000000000000000..7ce503c2dd97ba78597f6ff6e4393132753573f6 --- /dev/null +++ b/MisplaceAI/user_dashboard/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/MisplaceAI/user_dashboard/urls.py b/MisplaceAI/user_dashboard/urls.py new file mode 100644 index 0000000000000000000000000000000000000000..93f9170473eca27b05a49743362385633c4d9636 --- /dev/null +++ b/MisplaceAI/user_dashboard/urls.py @@ -0,0 +1,7 @@ +# MisplaceAI/user_dashboard/urls.py +from django.urls import path +from .views import UserDashboardView + +urlpatterns = [ + path('dashboard/', UserDashboardView.as_view(), name='user-dashboard'), +] diff --git a/MisplaceAI/user_dashboard/views.py b/MisplaceAI/user_dashboard/views.py new file mode 100644 index 0000000000000000000000000000000000000000..b428d17abd77958cd77cb373b06a6da89178afae --- /dev/null +++ b/MisplaceAI/user_dashboard/views.py @@ -0,0 +1,15 @@ +# MisplaceAI/user_dashboard/views.py +from django.contrib.auth.models import User +from rest_framework import generics +from rest_framework.permissions import IsAuthenticated +from .serializers import UserSerializer + +class UserDashboardView(generics.RetrieveAPIView): + queryset = User.objects.all() + serializer_class = UserSerializer + permission_classes = [IsAuthenticated] + + def get_object(self): + user = self.request.user + print(f'User: {user}') # Debugging statement + return user diff --git a/frontend/src/App.js b/frontend/src/App.js index 1f8039ea0470fc706816fdbb24fa0081dd9105dc..c472e5b4419867e7530f1cf0052c0691c238022e 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -13,6 +13,7 @@ import ManageItems from './pages/Items/ManageItemsPage'; import ManageRulesPage from './pages/Rules/ManageRulesPage'; import DetectionOptionsPage from './pages/DetectionOptions/DetectionOptionsPage'; import NormalDetectionPage from './pages/NormalDetection/NormalDetectionPage'; +import UserDashboard from './pages/UserDashboard/UserDashboard'; import ProtectedRoute from './firewall/ProtectedRoute'; import RouteProtection from './firewall/RouteProtection'; @@ -71,6 +72,11 @@ function App() { <NormalDetectionPage /> </ProtectedRoute> } /> + <Route path="/user/dashboard" element={ + <ProtectedRoute> + <UserDashboard /> + </ProtectedRoute> + } /> <Route path="*" element={<HomePage />} /> {/* Catch-all route */} </Routes> </div> diff --git a/frontend/src/layouts/Navbar/Navbar.js b/frontend/src/layouts/Navbar/Navbar.js index 12293a397a7949ecb354a13c515b9b8875e6f9bc..749ee3976bf29d57583d15660fb5162fdfa39c4a 100644 --- a/frontend/src/layouts/Navbar/Navbar.js +++ b/frontend/src/layouts/Navbar/Navbar.js @@ -31,9 +31,14 @@ const Navbar = () => { <Link className="nav-link" to="/">Home</Link> </li> {isAuthenticated && ( - <li className="nav-item"> - <Link className="nav-link" to="/detection-options">Detect Misplaced Items</Link> - </li> + <> + <li className="nav-item"> + <Link className="nav-link" to="/detection-options">Detect Misplaced Items</Link> + </li> + <li className="nav-item"> + <Link className="nav-link" to="/user/dashboard">User Dashboard</Link> + </li> + </> )} {!isAuthenticated && ( <> diff --git a/frontend/src/pages/UserDashboard/UserDashboard.css b/frontend/src/pages/UserDashboard/UserDashboard.css new file mode 100644 index 0000000000000000000000000000000000000000..e3fcffc2499b01335a89d1e0f35dbf9f8e1632d2 --- /dev/null +++ b/frontend/src/pages/UserDashboard/UserDashboard.css @@ -0,0 +1,17 @@ +/* src/pages/UserDashboard/UserDashboard.css */ + +.user-dashboard { + padding: 20px; + background-color: #f9f9f9; + border-radius: 8px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); +} + +.user-dashboard h1 { + margin-bottom: 20px; +} + +.user-dashboard .user-info p { + margin: 10px 0; + font-size: 16px; +} \ No newline at end of file diff --git a/frontend/src/pages/UserDashboard/UserDashboard.js b/frontend/src/pages/UserDashboard/UserDashboard.js new file mode 100644 index 0000000000000000000000000000000000000000..94af5999124065ca6ed5d7638d881e99ef34868a --- /dev/null +++ b/frontend/src/pages/UserDashboard/UserDashboard.js @@ -0,0 +1,44 @@ +// src/pages/UserDashboard/UserDashboard.js +import React, { useEffect, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import api from '../../services/api'; +import './UserDashboard.css'; + +const UserDashboard = () => { + const [userData, setUserData] = useState(null); + const navigate = useNavigate(); + + useEffect(() => { + const fetchUserData = async () => { + try { + const response = await api.get('/api/user_dashboard/dashboard/'); + setUserData(response.data); + } catch (error) { + console.error('Failed to fetch user data', error); + if (error.response && error.response.status === 401) { + navigate('/login'); + } + } + }; + + fetchUserData(); + }, [navigate]); + + if (!userData) { + return <div>Loading...</div>; + } + + return ( + <div className="user-dashboard"> + <h1>User Dashboard</h1> + <div className="user-info"> + <p><strong>Username:</strong> {userData.username}</p> + <p><strong>Email:</strong> {userData.email}</p> + <p><strong>First Name:</strong> {userData.first_name}</p> + <p><strong>Last Name:</strong> {userData.last_name}</p> + </div> + </div> + ); +}; + +export default UserDashboard; diff --git a/frontend/src/services/api.js b/frontend/src/services/api.js index 6ca18ff9690c3c23e923260942507041ece252e7..5e6e9d190c7ea233337be534bf2e769768c31be4 100644 --- a/frontend/src/services/api.js +++ b/frontend/src/services/api.js @@ -21,7 +21,7 @@ const api = axios.create({ api.interceptors.request.use( config => { - const token = localStorage.getItem('adminToken'); + const token = localStorage.getItem('token') || localStorage.getItem('adminToken'); const csrfToken = getCsrfToken(); if (token) { config.headers.Authorization = `Bearer ${token}`; diff --git a/frontend/src/services/auth.js b/frontend/src/services/auth.js index 8f3f0ead6a9d6b876b319cbb66383cef717bd230..73b599b7cf1727afde884b212b4354564dd5e2ca 100644 --- a/frontend/src/services/auth.js +++ b/frontend/src/services/auth.js @@ -1,10 +1,11 @@ -// frontend/src/services/auth.js +// src/services/auth.js import api from './api'; export const login = async (credentials) => { const response = await api.post('/api/auth/login/', credentials); if (response.data.access) { localStorage.setItem('token', response.data.access); + localStorage.setItem('isAuthenticated', true); } return response.data; }; @@ -16,6 +17,7 @@ export const register = async (userData) => { export const logout = () => { localStorage.removeItem('token'); + localStorage.removeItem('isAuthenticated'); }; export const getCurrentUser = async () => {