Skip to content

Commit

Permalink
Merge pull request #95 from DocShow-AI/implement_base_DataProfiling_page
Browse files Browse the repository at this point in the history
Implement base data profiling page
  • Loading branch information
liberty-rising authored Dec 11, 2023
2 parents 8b8a581 + 4a7a46c commit 46b8ee2
Show file tree
Hide file tree
Showing 8 changed files with 207 additions and 33 deletions.
21 changes: 18 additions & 3 deletions backend/databases/data_profile_manager.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from models.data_profile import DataProfile
from models.data_profile import DataProfile, DataProfileCreateRequest


class DataProfileManager:
Expand All @@ -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()
)
1 change: 1 addition & 0 deletions backend/envs/dev/initialization/setup_dev_environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
8 changes: 5 additions & 3 deletions backend/models/data_profile.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from sqlalchemy import Column, Integer, String, ForeignKey
from pydantic import BaseModel

from .base import Base


Expand All @@ -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.
"""

Expand All @@ -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):
"""
Expand All @@ -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
12 changes: 12 additions & 0 deletions backend/routes/data_profile_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
6 changes: 5 additions & 1 deletion frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@ 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';
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 (
Expand Down Expand Up @@ -69,6 +71,8 @@ function App() {
<Route path="/upload" element={<RequireAuth><AppLayout><UploadPage /></AppLayout></RequireAuth>} />
<Route path="/analytics" element={<RequireAuth><AppLayout><AnalyticsPage /></AppLayout></RequireAuth>} />
<Route path="/data-profiling" element={<RequireAuth><AppLayout><DataProfilingPage /></AppLayout></RequireAuth>} />
<Route path="/data-profiling/:dataProfileId" element={<RequireAuth><AppLayout><SpecificDataProfilePage /></AppLayout></RequireAuth>} />
<Route path="/data-profiling/create" element={<RequireAuth><AppLayout><CreateDataProfile /></AppLayout></RequireAuth>} />
<Route path="/user" element={<RequireAuth><AppLayout><UserPage /></AppLayout></RequireAuth>} />
<Route path="/admin" element={<RequireAuth><AppLayout><AdminPage /></AppLayout></RequireAuth>} />
<Route path="/logout" element={<RequireAuth><Logout /></RequireAuth>} />
Expand Down
58 changes: 58 additions & 0 deletions frontend/src/pages/DataProfiling/CreateDataProfile.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<Box component="form" onSubmit={handleSubmit} noValidate sx={{ mt: 1 }}>
<Typography variant="h6">Create New Data Profile</Typography>
<TextField
required
fullWidth
label="Name"
value={name}
onChange={e => setName(e.target.value)}
margin="normal"
/>
<TextField
required
fullWidth
label="Description"
value={description}
onChange={e => setDescription(e.target.value)}
margin="normal"
/>
<Button type="submit" fullWidth variant="contained" sx={{ mt: 3, mb: 2 }}>
Create Data Profile
</Button>
<Button fullWidth variant="outlined" sx={{ mt: 1 }} onClick={handleBack}>
Back to Data Profiling
</Button>
</Box>
);
};

export default CreateDataProfile;
66 changes: 40 additions & 26 deletions frontend/src/pages/DataProfiling/DataProfiling.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<Box>
<Typography variant="h4" gutterBottom>🔍 Data Profiling</Typography>
<FormControl fullWidth>
<InputLabel id="data-profile-select-label">Data Profile</InputLabel>
<Select
labelId="data-profile-select-label"
id="data-profile-select"
value={selectedProfile}
label="Data Profile"
onChange={handleChange}
>
{dataProfiles.map((profile, index) => (
<MenuItem key={index} value={profile.id}>
{profile.name}
</MenuItem>
))}
</Select>
</FormControl>
<Button
variant="contained"
color="primary"
onClick={handleCreateNewProfile}
sx={{ mb: 2 }}
>
Create New Data Profile
</Button>
<TableContainer component={Paper}>
<Table>
<TableHead>
<TableRow>
<TableCell>Name</TableCell>
<TableCell>Description</TableCell>
</TableRow>
</TableHead>
<TableBody>
{dataProfiles.map((profile) => (
<TableRow key={profile.id}>
<TableCell>
<Link component="button" variant="body2" onClick={() => handleProfileClick(profile.id)}>
{profile.name}
</Link>
</TableCell>
<TableCell>{profile.description}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</Box>
);
}
Expand Down
68 changes: 68 additions & 0 deletions frontend/src/pages/DataProfiling/SpecificDataProfilePage.jsx
Original file line number Diff line number Diff line change
@@ -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 <CircularProgress />;
}

if (error) {
return <Typography variant="h6" color="error">Error loading profile</Typography>;
}

return (
<Box>
<Typography variant="h4" gutterBottom>Data Profile Details</Typography>
<TableContainer component={Paper}>
<Table>
<TableBody>
<TableRow>
<TableCell>ID</TableCell>
<TableCell>{profile.id}</TableCell>
</TableRow>
<TableRow>
<TableCell>Name</TableCell>
<TableCell>{profile.name}</TableCell>
</TableRow>
<TableRow>
<TableCell>File Type</TableCell>
<TableCell>{profile.file_type}</TableCell>
</TableRow>
<TableRow>
<TableCell>Organization ID</TableCell>
<TableCell>{profile.organization_id}</TableCell>
</TableRow>
<TableRow>
<TableCell>Description</TableCell>
<TableCell>{profile.description}</TableCell>
</TableRow>
{/* Add more rows as needed */}
</TableBody>
</Table>
</TableContainer>
</Box>
);
}

export default SpecificDataProfilePage;

0 comments on commit 46b8ee2

Please sign in to comment.