diff --git a/backend/databases/data_profile_manager.py b/backend/databases/data_profile_manager.py index 9f3a84d..3c48bf7 100644 --- a/backend/databases/data_profile_manager.py +++ b/backend/databases/data_profile_manager.py @@ -1,4 +1,4 @@ -from models.data_profile import DataProfile +from models.data_profile import DataProfile, DataProfileCreateRequest class DataProfileManager: @@ -13,7 +13,22 @@ def get_all_data_profiles(self): """Retrieve all DataProfiles.""" return self.session.query(DataProfile).all() - def create_dataprofile(self, data_profile): + def create_dataprofile(self, data_profile_data: DataProfileCreateRequest): """Create a new DataProfile.""" - self.session.add(data_profile) + new_data_profile = DataProfile( + name=data_profile_data.name, + file_type=data_profile_data.file_type, # Assuming it's included in the request + organization_id=data_profile_data.organization_id, # Assuming it's included in the request + description=data_profile_data.description, + ) + self.session.add(new_data_profile) self.session.commit() + return new_data_profile.to_dict() + + def get_dataprofile_by_id(self, data_profile_id: int): + """Retrieve a DataProfile by its ID.""" + return ( + self.session.query(DataProfile) + .filter(DataProfile.id == data_profile_id) + .first() + ) diff --git a/backend/envs/dev/initialization/setup_dev_environment.py b/backend/envs/dev/initialization/setup_dev_environment.py index 0ae1186..ec5caef 100644 --- a/backend/envs/dev/initialization/setup_dev_environment.py +++ b/backend/envs/dev/initialization/setup_dev_environment.py @@ -172,6 +172,7 @@ def create_sample_dataprofile(): name="Sample Profile", file_type="pdf", organization_id=1, + description="Sample Description", ) # Using DatabaseManager to manage the database session with DatabaseManager() as session: diff --git a/backend/models/data_profile.py b/backend/models/data_profile.py index b8b8530..3757a8c 100644 --- a/backend/models/data_profile.py +++ b/backend/models/data_profile.py @@ -1,6 +1,5 @@ from sqlalchemy import Column, Integer, String, ForeignKey from pydantic import BaseModel - from .base import Base @@ -10,13 +9,12 @@ class DataProfile(Base): DataProfile Model ----------------- This class represents the 'dataprofile' table in the database. - Attributes: - id: A unique identifier for each data profile. - name: The name of the data profile. - file_type: The type of file associated with the data profile. - organization_id: The organization associated with the data profile. - + - description: The description of the data profile. The class also is converting the model instance into a dictionary. """ @@ -25,6 +23,7 @@ class DataProfile(Base): name = Column(String) file_type = Column(String) organization_id = Column(Integer, ForeignKey("organizations.id")) + description = Column(String) # New description column def to_dict(self): """ @@ -35,12 +34,15 @@ def to_dict(self): "name": self.name, "file_type": self.file_type, "organization_id": self.organization_id, + "description": self.description, } class DataProfileCreateRequest(BaseModel): name: str + description: str class DataProfileCreateResponse(BaseModel): name: str + description: str diff --git a/backend/routes/data_profile_routes.py b/backend/routes/data_profile_routes.py index 649bbad..06722c4 100644 --- a/backend/routes/data_profile_routes.py +++ b/backend/routes/data_profile_routes.py @@ -35,3 +35,15 @@ async def save_data_profiles( created_data_profile = data_profile_manager.create_dataprofile(new_data_profile) response = DataProfileCreateResponse(created_data_profile.name) return response + + +@data_profile_router.get("/data-profiles/{data_profile_id}") +async def get_data_profile( + data_profile_id: int, current_user: User = Depends(get_current_user) +): + with DatabaseManager() as session: + data_profile_manager = DataProfileManager(session) + data_profile = data_profile_manager.get_dataprofile_by_id(data_profile_id) + if data_profile is None: + raise HTTPException(status_code=404, detail="Data Profile not found") + return data_profile diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index e484d56..0d05d5a 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -19,6 +19,9 @@ import CreateChartPage from './pages/Charts/CreateChart'; import CreateDashboardPage from './pages/Dashboards/CreateDashboard'; import DashboardMenuPage from './pages/Dashboards/DashboardsMenuPage'; import Dashboard from './pages/Dashboards/Dashboard'; +import DataProfilingPage from './pages/DataProfiling/DataProfiling'; +import CreateDataProfile from './pages/DataProfiling/CreateDataProfile'; +import SpecificDataProfilePage from './pages/DataProfiling/SpecificDataProfilePage'; import LandingPage from './pages/Landing'; import LoginPage from './pages/Login'; import PricingPage from './pages/Pricing'; @@ -26,7 +29,6 @@ import RegisterPage from './pages/Register'; import UploadPage from './pages/Upload/Upload'; import UserPage from './pages/User'; import Logout from './pages/Logout'; -import DataProfilingPage from './pages/DataProfiling/DataProfiling'; function AppWrapper() { return ( @@ -69,6 +71,8 @@ function App() { } /> } /> } /> + } /> + } /> } /> } /> } /> diff --git a/frontend/src/pages/DataProfiling/CreateDataProfile.jsx b/frontend/src/pages/DataProfiling/CreateDataProfile.jsx new file mode 100644 index 0000000..af1a251 --- /dev/null +++ b/frontend/src/pages/DataProfiling/CreateDataProfile.jsx @@ -0,0 +1,58 @@ +import React, { useState } from 'react'; +import { Box, TextField, Button, Typography } from '@mui/material'; +import axios from 'axios'; +import { useNavigate } from 'react-router-dom'; +import { API_URL } from '../../utils/constants'; + +const CreateDataProfile = () => { + const [name, setName] = useState(''); + const [description, setDescription] = useState(''); + const navigate = useNavigate(); + + const handleSubmit = (event) => { + event.preventDefault(); + axios.post(`${API_URL}data-profile/`, { name, description }) + .then(response => { + // Handle successful data profile creation + console.log('Data Profile created:', response.data); + navigate('/data-profiling') + }) + .catch(error => { + console.error('Error creating data profile:', error); + }); + }; + + const handleBack = () => { + navigate('/data-profiling') + } + + return ( + + Create New Data Profile + setName(e.target.value)} + margin="normal" + /> + setDescription(e.target.value)} + margin="normal" + /> + + + + ); +}; + +export default CreateDataProfile; diff --git a/frontend/src/pages/DataProfiling/DataProfiling.jsx b/frontend/src/pages/DataProfiling/DataProfiling.jsx index 193ccb5..f501e34 100644 --- a/frontend/src/pages/DataProfiling/DataProfiling.jsx +++ b/frontend/src/pages/DataProfiling/DataProfiling.jsx @@ -1,53 +1,67 @@ import React, { useState, useEffect } from 'react'; import axios from 'axios'; -import { Box, Typography, Select, MenuItem, FormControl, InputLabel, Button } from '@mui/material'; +import { Box, Typography, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, Link, Button } from '@mui/material'; +import { useNavigate } from 'react-router-dom'; import { API_URL } from '../../utils/constants'; function DataProfilingPage() { const [dataProfiles, setDataProfiles] = useState([]); - const [selectedProfile, setSelectedProfile] = useState(''); + const navigate = useNavigate(); useEffect(() => { axios.get(`${API_URL}data-profiles/`) .then(response => { - // Check if the response is an array before updating state if (Array.isArray(response.data)) { setDataProfiles(response.data); } else { console.error('Received data is not an array:', response.data); - // Optionally set an empty array or handle this scenario appropriately setDataProfiles([]); } }) - .catch(error => { - console.error('Error fetching data profiles:', error); - setDataProfiles([]); // Reset to empty array on error - }); + .catch(error => console.error('Error fetching data profiles:', error)); }, []); - const handleChange = (event) => { - setSelectedProfile(event.target.value); + const handleProfileClick = (dataProfileId) => { + navigate(`/data-profiling/${dataProfileId}`); + }; + + const handleCreateNewProfile = () => { + navigate('/data-profiling/create'); }; return ( 🔍 Data Profiling - - Data Profile - - + + + + + + Name + Description + + + + {dataProfiles.map((profile) => ( + + + handleProfileClick(profile.id)}> + {profile.name} + + + {profile.description} + + ))} + +
+
); } diff --git a/frontend/src/pages/DataProfiling/SpecificDataProfilePage.jsx b/frontend/src/pages/DataProfiling/SpecificDataProfilePage.jsx new file mode 100644 index 0000000..9a79552 --- /dev/null +++ b/frontend/src/pages/DataProfiling/SpecificDataProfilePage.jsx @@ -0,0 +1,68 @@ +import React, { useState, useEffect } from 'react'; +import { useParams } from 'react-router-dom'; +import axios from 'axios'; +import { Box, Typography, CircularProgress, Table, TableBody, TableCell, TableRow, TableContainer, Paper } from '@mui/material'; +import { API_URL } from '../../utils/constants'; + +function SpecificDataProfilePage() { + const { dataProfileId } = useParams(); + const [profile, setProfile] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + axios.get(`${API_URL}/data-profiles/${dataProfileId}`) + .then(response => { + setProfile(response.data); + setLoading(false); + }) + .catch(error => { + console.error('Error fetching data profile:', error); + setError(error); + setLoading(false); + }); + }, [dataProfileId]); + + if (loading) { + return ; + } + + if (error) { + return Error loading profile; + } + + return ( + + Data Profile Details + + + + + ID + {profile.id} + + + Name + {profile.name} + + + File Type + {profile.file_type} + + + Organization ID + {profile.organization_id} + + + Description + {profile.description} + + {/* Add more rows as needed */} + +
+
+
+ ); +} + +export default SpecificDataProfilePage;