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;
});