From 0043480ff877ee57a401ee2ba793f405e30b752b Mon Sep 17 00:00:00 2001 From: James Ketrenos Date: Tue, 17 Jan 2023 20:58:12 -0800 Subject: [PATCH] Face removal working Signed-off-by: James Ketrenos --- client/src/App.css | 5 ++ client/src/App.tsx | 105 +++++++++++++++++++++++------------- server/db/photos.js | 5 ++ server/routes/identities.js | 37 +++++++++++++ 4 files changed, 115 insertions(+), 37 deletions(-) diff --git a/client/src/App.css b/client/src/App.css index cd08146..b79dbe8 100644 --- a/client/src/App.css +++ b/client/src/App.css @@ -56,6 +56,11 @@ div { border: 0.25rem solid transparent; } +.ClusterEditor { + display: flex; + flex-direction: column; +} + .Image .FaceBox { border: 1px solid red; /* border-radius: 0.25rem;*/ diff --git a/client/src/App.tsx b/client/src/App.tsx index 523dc06..5e46c48 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -89,10 +89,10 @@ const Photo = ({ photoId }: any) => { ); }; -const Face = ({ faceId, onClick, title }: any) => { +const Face = ({ faceId, onClick, title, ...rest }: any) => { const idPath = String(faceId % 100).padStart(2, '0'); return ( -
{ onClick(e, faceId) }} +
{ onClick(e, faceId) }} className='Face'>
{ }; type ClusterProps = { - id: number, - setImage(image: number): void + identity: Identity, + setImage(image: number): void, + setSelected(selected: number[]): void, + setIdentity(identity: Identity): void }; -const Cluster = ({ id, setImage }: ClusterProps) => { - const [identity, setIdentity] = useState(undefined); - const { loading, data } = useApi( - `../api/v1/identities/${id}` - ); - - useEffect(() => { - if (data) { - if (Array.isArray(data) && data.length > 0) { - setIdentity(data[0] as Identity); - } else { - setIdentity(data as Identity); - } - } - }, [data]); - +const Cluster = ({ identity, setIdentity, setImage, setSelected }: ClusterProps) => { const relatedFacesJSX = useMemo(() => { const faceClicked = async (e: any, id: any) => { if (!identity) { @@ -142,18 +129,25 @@ const Cluster = ({ id, setImage }: ClusterProps) => { return; } el.classList.toggle('Selected'); + const selected = [...el.parentElement + .querySelectorAll('.Selected')] + .map((face: any) => face.getAttribute('data-face-id')); + setSelected(selected); console.log(face); } if (identity === undefined) { return <>; } + return identity.relatedFaces.map(face => - ); - }, [identity, setImage]); + }, [identity, setImage, setSelected]); const lastNameChanged = (e: any) => { setIdentity(Object.assign( @@ -188,12 +182,6 @@ const Cluster = ({ id, setImage }: ClusterProps) => { )); }; - if (loading) { - return (
- {loading && `Loading ${id}...`} -
); - } - if (identity === undefined) { return (
Select identity to load. @@ -257,21 +245,23 @@ type Identity = { }; interface IdentitiesProps { - setIdentity?(id: number): void, + setIdentity(identity: Identity): void, identities: Identity[] }; const Identities = ({ identities, setIdentity } : IdentitiesProps) => { const identitiesJSX = useMemo(() => { - const loadIdentity = (id: number): void => { - if (setIdentity) { - setIdentity(id) - } + const loadIdentity = async (id: number) => { + const res = await window.fetch(`../api/v1/identities/${id}`); + const data = await res.json(); + setIdentity(data[0]); }; - return identities.map((identity) => { + + return identities.map((identity) => { const face = identity.relatedFaces[0]; return ( loadIdentity(identity.id)} title={identity.displayName}/> @@ -286,13 +276,22 @@ const Identities = ({ identities, setIdentity } : IdentitiesProps) => { ); }; +const Button = ({ onClick, children }: any) => { + return ( + + ); +}; + const App = () => { const [identities, setIdentities] = useState([]); - const [identity, setIdentity] = useState(0); + const [identity, setIdentity] = useState(undefined); const [image, setImage] = useState(0); const { loading, data } = useApi( '../api/v1/identities' ); + const [selected, setSelected] = useState([]); useEffect(() => { if (data && data.length) { @@ -300,17 +299,49 @@ const App = () => { } }, [data]); + const removeSelected = async () => { + try { + const res = await window.fetch( + `../api/v1/identities/faces/remove/${identity.id}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ faces: selected }) + }); + const data = await res.json(); + + const pre = identity.relatedFaces.length; + /* Remove all relatedFaces which are part of the set of removed + * faces */ + identity.relatedFaces = identity.relatedFaces.filter( + (face: FaceData) => data.faces.indexOf(face.faceId) === -1); + if (pre !== identity.relatedFaces.length) { + setIdentity({...identity}) + } + } catch (error) { + console.error(error); + } + }; + return (
- + {loading &&
Loading...
} - {!loading && identity !== 0 && } + {!loading && identity !== 0 && + } {!loading && identity === 0 &&
Select identity to edit
} +
+ { selected.length !== 0 && } +
diff --git a/server/db/photos.js b/server/db/photos.js index 72b58b5..f3cf139 100755 --- a/server/db/photos.js +++ b/server/db/photos.js @@ -172,6 +172,11 @@ function init() { key: 'id', } }, + + expertAssignment: { + type: Sequelize.BOOLEAN, + defaultValue: false + }, lastComparedId: { type: Sequelize.INTEGER, diff --git a/server/routes/identities.js b/server/routes/identities.js index 45a5ca4..156377e 100755 --- a/server/routes/identities.js +++ b/server/routes/identities.js @@ -11,6 +11,42 @@ require("../db/photos").then(function(db) { const router = express.Router(); + +router.put("/faces/remove/:id", (req, res) => { + console.log(`PUT ${req.url}`) + if (!req.user.maintainer) { + console.warn(`${req.user.name} attempted to modify photos.`); + return res.status(401).send({ message: "Unauthorized to modify photos." }); + } + + const id = parseInt(req.params.id); + if (id != req.params.id) { + return res.status(400).send({ message: "Invalid identity id." }); + } + + if (!Array.isArray(req.body.faces) || req.body.faces.length == 0) { + return res.status(400).send({ message: "No faces supplied." }); + } + + return photoDB.sequelize.query( + "UPDATE faces SET identityId=null " + + "WHERE id IN (:faceIds)", { + replacements: { + identityId: id, + faceIds: req.body.faces + } + }).then(() => { + const identity = { + id: id, + faces: req.body.faces + }; + return res.status(200).json(identity); + }).catch((error) => { + console.error(error); + return res.status(500).send({message: "Error processing request." }); + }); +}); + router.put("/faces/add/:id", (req, res) => { if (!req.user.maintainer) { console.warn(`${req.user.name} attempted to modify photos.`); @@ -182,6 +218,7 @@ router.get("/:id?", async (req, res) => { delete identity.descriptors; delete identity.relatedFaceIds; delete identity.relatedFacePhotoIds; + delete identity.relatedFaceDescriptorIds; delete identity.relatedIdentityDescriptors; });