diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..17e15f27 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python: Current File", + "type": "python", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal" + } + ] +} \ No newline at end of file diff --git a/backend/family_tree/node_modules/django/README.md b/backend/family_tree/node_modules/django/README.md new file mode 100644 index 00000000..b5932373 --- /dev/null +++ b/backend/family_tree/node_modules/django/README.md @@ -0,0 +1,5 @@ +# Security holding package + +This package name is not currently in use, but was formerly occupied +by another package. To avoid malicious use, npm is hanging on to the +package name. diff --git a/backend/family_tree/node_modules/django/package.json b/backend/family_tree/node_modules/django/package.json new file mode 100644 index 00000000..3769a88f --- /dev/null +++ b/backend/family_tree/node_modules/django/package.json @@ -0,0 +1,39 @@ +{ + "_from": "django", + "_id": "django@1.0.0", + "_inBundle": false, + "_integrity": "sha512-PWiLJnnmf+Mghtm1J/YCZMQLM+N8rrz+bBgchOGM9ldJx9IlXM0D8h4u0vfCM85XafEbWAE8JyFbESykgiBhEw==", + "_location": "/django", + "_phantomChildren": {}, + "_requested": { + "type": "tag", + "registry": true, + "raw": "django", + "name": "django", + "escapedName": "django", + "rawSpec": "", + "saveSpec": null, + "fetchSpec": "latest" + }, + "_requiredBy": [ + "#USER", + "/" + ], + "_resolved": "https://registry.npmjs.org/django/-/django-1.0.0.tgz", + "_shasum": "e90c7b91b8681555317545ba8ea69bc8a0a148c8", + "_spec": "django", + "_where": "C:\\Users\\acer\\OneDrive\\Desktop\\Programming\\iitj_family_tree\\backend\\family_tree", + "bugs": { + "url": "https://github.com/npm/security-holder/issues" + }, + "bundleDependencies": false, + "deprecated": "django is now django-express on request of the Django Software Foundation.", + "description": "security holding package", + "homepage": "https://github.com/npm/security-holder#readme", + "name": "django", + "repository": { + "type": "git", + "url": "git+https://github.com/npm/security-holder.git" + }, + "version": "1.0.0" +} diff --git a/backend/family_tree/package-lock.json b/backend/family_tree/package-lock.json new file mode 100644 index 00000000..bfd3fe55 --- /dev/null +++ b/backend/family_tree/package-lock.json @@ -0,0 +1,11 @@ +{ + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "django": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/django/-/django-1.0.0.tgz", + "integrity": "sha512-PWiLJnnmf+Mghtm1J/YCZMQLM+N8rrz+bBgchOGM9ldJx9IlXM0D8h4u0vfCM85XafEbWAE8JyFbESykgiBhEw==" + } + } +} diff --git a/frontend/family_tree/src/App.css b/frontend/family_tree/src/App.css index ec1735f0..a625f8f1 100644 --- a/frontend/family_tree/src/App.css +++ b/frontend/family_tree/src/App.css @@ -47,3 +47,18 @@ width: 150px; height: 170px; } + +.button { + border: 1px; + border-radius: 6px; + cursor: pointer; + font-family: "JetBrains Mono"; + overflow: hidden; + text-decoration: none; + transition: box-shadow .15s, transform .15s; + will-change: box-shadow, transform; +} + +.button:active { + transform: translateY(1px); +} diff --git a/frontend/family_tree/src/App.js b/frontend/family_tree/src/App.js index c0db6ddb..3a12df51 100644 --- a/frontend/family_tree/src/App.js +++ b/frontend/family_tree/src/App.js @@ -2,12 +2,11 @@ import React, { useEffect, useState } from "react"; import './App.css'; import Help from './Components/Help.js'; import SearchBar from "./Components/SearchBar"; -import StudentData from "./Data.json"; import PCard from "./Components/ProfileCard.js"; import { ThemeProvider, createTheme } from "@material-ui/core"; import D3Tree from "./Components/Tree"; import {client} from "./index.js"; -import {CHILDREN_QUERY, BATCH_QUERY, PATH_QUERY} from "./Queries.js"; +import {PATH_QUERY} from "./Queries.js"; function App() { @@ -19,9 +18,10 @@ function App() { } }); - const [details, setDetails] = useState({ name:"name", branch:"branch", year:"year", email:"email", picture:"picture", linkedIn:"", hometown:"", coCurriculars:"", socialMedia:"", display:true}); + const [details, setDetails] = useState({ name:"IIT JODHPUR", branch:"FAMILY", year:"TREE", email:"email", picture:"picture", linkedIn:"", hometown:"", coCurriculars:"", socialMedia:"", display:false}); const [TreeData, setTreeData] = useState({}); - + const [clickedNode, setClickedNode] = useState(""); + async function FetchPath(rollNo) { const response = await client.query({ query: PATH_QUERY, @@ -33,14 +33,13 @@ function App() { } useEffect(() => { - (FetchPath("B20AI054")).then(value => {setTreeData(value);}) - , [TreeData]}) - + (FetchPath("Root")).then(value => {setTreeData(value);}) + },[TreeData]) return (
- +
@@ -48,6 +47,7 @@ function App() { }
diff --git a/frontend/family_tree/src/Components/ProfileCard.js b/frontend/family_tree/src/Components/ProfileCard.js index dfc24c5a..1780eda0 100644 --- a/frontend/family_tree/src/Components/ProfileCard.js +++ b/frontend/family_tree/src/Components/ProfileCard.js @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useEffect } from "react"; import { Typography, Card, @@ -43,15 +43,18 @@ const useStyles = makeStyles((theme) => ({ function PCard(props) { const classes = useStyles(); - const [isVisible, setisVisible] = React.useState(props.display?true:false); - const toggle = () => setisVisible(!isVisible); + const [isVisible, setisVisible] = React.useState(false); + useEffect(()=>{ + setisVisible(true); + },[props.display]) + return ( { isVisible && ( - + {setisVisible(false);}} > @@ -113,7 +116,7 @@ function PCard(props) {
- {props.coCurriculars.map((item) => ( + {props.coCurriculars.split(",").map((item) => ( {item} @@ -130,7 +133,7 @@ function PCard(props) { flexWrap: "wrap" }} > - + {/* {props.email && ( )} - + */}
)} diff --git a/frontend/family_tree/src/Components/SearchBar.js b/frontend/family_tree/src/Components/SearchBar.js index 12d320bf..fcc73b17 100644 --- a/frontend/family_tree/src/Components/SearchBar.js +++ b/frontend/family_tree/src/Components/SearchBar.js @@ -1,30 +1,43 @@ import React, { useState } from "react"; import "../Styles/SearchBar.css"; import SearchIcon from "@material-ui/icons/Search"; -import CloseIcon from "@material-ui/icons/Close" +import CloseIcon from "@material-ui/icons/Close"; +import {client} from "../index.js"; +import {SEARCH_QUERY} from "../Queries.js"; -function SearchBar({ placeholder, studentData }) { - const [filteredData, setFilteredData] = useState([]); +function SearchBar({ placeholder, setClickedNode}) { const [wordEntered, setWordEntered] = useState(""); + const [retrievedData, setRetrievedData] = useState([]); + + async function FetchString(string) { + const response = await client.query({ + query: SEARCH_QUERY, + variables: { + string, + }, + }) + return response.data.studentSearch; + } const handleFilter = (event) => { const searchWord = event.target.value; setWordEntered(searchWord); if (searchWord.length < 3) { - setFilteredData([]); + setRetrievedData([]); } else { - const filteredResults = studentData.filter((query) => { - return query.student.name.toLowerCase().includes(searchWord.toLowerCase()) + query.student.id.toLowerCase().includes(searchWord.toLowerCase()); - }); - setFilteredData(filteredResults); + FetchString(searchWord).then(value => setRetrievedData(value)); } }; const clearState = () => { - setFilteredData([]); + setRetrievedData([]); setWordEntered(""); }; + var getRollNo = (e) => { + setClickedNode(e.target.innerHTML.slice(-10,-2)); + } + return (
@@ -42,11 +55,11 @@ function SearchBar({ placeholder, studentData }) { )}
- {filteredData.length !== 0 && ( + {retrievedData.length !== 0 && (
- {filteredData.slice(0, 5).map((value) => { + {retrievedData.slice(0, 5).map((value) => { return ( -

{`${value.student.name} (${value.student.id})`}

+

{`${value.name} (${value.rollNo})`}

); })}
@@ -55,4 +68,4 @@ function SearchBar({ placeholder, studentData }) { ); } -export default SearchBar; +export default SearchBar; \ No newline at end of file diff --git a/frontend/family_tree/src/Components/Tree.js b/frontend/family_tree/src/Components/Tree.js index df5e231a..41861ea9 100644 --- a/frontend/family_tree/src/Components/Tree.js +++ b/frontend/family_tree/src/Components/Tree.js @@ -1,8 +1,64 @@ import * as d3 from 'd3'; -import StudentData from "../Data1.json"; -import React, { useLayoutEffect } from "react"; +import React, { useEffect, useLayoutEffect, useState } from "react"; +import {CHILDREN_QUERY, PATH_QUERY, NODE_DETAILS_QUERY} from "../Queries.js"; +import {client} from "../index.js"; function D3Tree(props){ + var data = props.TreeData; + var stratify = d3.stratify().id(d=>d.rollNo).parentId(d=>d.parentId) ; + var root = stratify(data); + + async function FetchPath(rollNo) { + const response = await client.query({ + query: PATH_QUERY, + variables: { + rollNo, + }, + }) + return response.data.studentPath; + } + + async function FetchChildren(parentId) { + const response = await client.query({ + query: CHILDREN_QUERY, + variables: { + parentId, + }, + }) + return response.data.children; + } + + async function FetchNodeDetails(rollNo) { + const response = await client.query({ + query: NODE_DETAILS_QUERY, + variables: { + rollNo, + }, + }) + return response.data.studentNode; + } + + // useEffect(()=>{ + // FetchPath(props.clickedNode).then((value)=> { + // for(var i=value.length-1; i>=0; i--){ + // if(value[i].rollNo!==props.clickedNode){ + // FetchChildren(value[i].rollNo).then((value1)=>{ + // for(var i=0; i temp.rollNo===value1[i].rollNo)); + // if(!hasValue){ + // data = data.concat(value1); + // console.log(data); + // break; + // } + // } + // }); + // } + // } + // root = stratify(data); + // update(root); + // }) + // },[props.clickedNode,data]) + useLayoutEffect(() =>{ var width = window.innerWidth; var height = window.innerHeight; @@ -14,13 +70,9 @@ function D3Tree(props){ })) .append("g") .attr("transform", "translate(" + width/2 + "," + height/3 + ")"); - var data = props.TreeData; var treemap = d3.tree().size([height,width]).nodeSize([120, 40]); - var stratify = d3.stratify().id(d=>d.rollNo).parentId(d=>d.parentId) ; - var root = stratify(data); - var i = 0; var duration = 750; @@ -28,7 +80,9 @@ function D3Tree(props){ root.y0 = 0; if(root.children){ - root.children.forEach(collapse);} + root.children.forEach(collapse); + } + function collapse(d) { if(d.children) { d._children = d.children @@ -38,9 +92,8 @@ function D3Tree(props){ } update(root); - function update(source) { + function update(source){ var treeData = treemap(root); - var nodes = treeData.descendants(), links = treeData.descendants().slice(1); @@ -48,28 +101,71 @@ function D3Tree(props){ d.y = d.depth * 180 ; }); - var node = svg.selectAll('g.node') .data(nodes, function(d) {return d.id || (d.id = ++i); }); + var display = false; + var nodeEnter = node.enter().append('g') .attr('class', 'node') .attr("transform", function(d) { return "translate(" + source.x0 + "," + source.y0 + ")"; }) .on('click', click) + .on("mousedown", function(d,node) { + if(d.button==0){ + updateChildren(d,node) + } + // var g = d3.select(this); + // if(g.property("childNodes").length<3) { + // g.append('circle') + // .attr('class', 'button') + // .attr('fill', 'gray') + // .attr('r', 10) + // .attr("cx", -10) + // .attr("cy", -14); + // g.select('.button') + // .append('animate') + // .classed('animate', true) + // .attr('attributeName', 'r') + // .attr('values', '0;10') + // .attr('begin', 'indefinite') + // .attr('dur', '0.2s') + // .attr('repeatCount', 1); + // g.append('text') + // .classed('button', true) + // .attr('x', -16) + // .attr('y', -10) + // .text("FB") + // .style("border", "solid") + // .style("stroke", "white") + // .style("cursor", "pointer") + // .on('click', test); + // g._groups[0][0].getElementsByTagName("animate")[0].beginElement(); + // }else{ + // g.selectAll('.button').style("visibility", "visible"); + // } + }) + .on("mouseout", function() { + d3.select(this).selectAll('.button').style("visibility", "hidden"); + }) .on('contextmenu', function(node,d){ - props.setDetails({name: d.id, - branch: d.data.branch, - year: d.data.year, - email: d.data.email, - picture: d.data.picture, - linkedIn: d.data.linkedIn, - hometown: d.data.hometown, - coCurriculars: d.data.coCurriculars, - socialMedia: d.data.socialMedia, - display: true - }); + node.preventDefault(); + display = !display; + FetchNodeDetails(d.data.rollNo).then((value)=>{ + props.setDetails({name: d.data.name, + branch: value.branch, + year: value.year, + email: value.email, + picture: value.picture, + linkedIn: value.linkedIn, + hometown: value.homeTown, + coCurriculars: value.extraCurriculars, + // socialMedia: value.socialMedia, + display: display, + }); + }) + }); nodeEnter.append('circle') @@ -77,6 +173,12 @@ function D3Tree(props){ .attr('r', 1e-6) .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; + }) + .on('mouseover',(d)=>{ + var g=d.target.parentNode + if(g.childNodes.length>3){ + g.getElementsByTagName("animate")[0].beginElement(); + } }); nodeEnter.append('text') @@ -157,8 +259,14 @@ function D3Tree(props){ ${d.x} ${d.y}` return path; } + } - function click(d,node) { + function test(){ + console.log("clicked"); + } + + function click(d,node) { + setTimeout(()=> { if (node.children) { node._children = node.children; node.children = null; @@ -167,6 +275,24 @@ function D3Tree(props){ node._children = null; } update(node); + },100) + } + + function updateChildren(d,node){ + if(!node.children && !node._children){ + FetchChildren(node.data.rollNo) + .then(value=> { + if(value.length!==0){ + for(var i=0; i temp.rollNo===value[i].rollNo)); + if(!hasValue){ + data = data.concat(value); + break; + } + } + root = stratify(data); + } + }) } } },[]) diff --git a/frontend/family_tree/src/Queries.js b/frontend/family_tree/src/Queries.js index cc04a5cf..8afde251 100644 --- a/frontend/family_tree/src/Queries.js +++ b/frontend/family_tree/src/Queries.js @@ -14,9 +14,9 @@ export const CHILDREN_QUERY = gql ` export const BATCH_QUERY = gql ` query StudentType($rollNo: String!) { studentBatch(roll: $rollNo) { - id, name, rollNo, + id, parentId, } } @@ -25,10 +25,33 @@ export const BATCH_QUERY = gql ` export const PATH_QUERY = gql ` query StudentType($rollNo: String!) { studentPath(roll: $rollNo) { - id, name, rollNo, + id, parentId, } } +`; + +export const SEARCH_QUERY = gql ` + query StudentType($string: String!) { + studentSearch(searchQuery: $string) { + name, + rollNo, + } + } +`; + +export const NODE_DETAILS_QUERY = gql ` + query StudentType($rollNo: String!) { + studentNode(roll: $rollNo) { + branch, + year, + picture, + homeTown, + extraCurriculars, + linkedIn, + email, + } + } `; \ No newline at end of file