Identities are loading; starting to think through workflow

Signed-off-by: James Ketrenos <james_git@ketrenos.com>
This commit is contained in:
James Ketr 2023-01-16 18:11:33 -08:00
parent 1ed1b1d1ea
commit e8de846ed0
4 changed files with 146 additions and 56 deletions

View File

@ -26,20 +26,30 @@ div {
border: 1px solid green; border: 1px solid green;
} }
.Identity { .Face {
display: flex; display: flex;
box-sizing: border-box;
flex-direction: column; flex-direction: column;
position: relative; position: relative;
margin: 0.125rem;
border: 1px solid transparent;
} }
.Identity:hover { .Face:hover {
border: 1px solid yellow;
cursor: pointer; cursor: pointer;
} }
.Identity .Title { .Face .Image {
border: 0.25rem solid transparent;
}
.Face:hover .Image {
border: 0.25rem solid yellow;
}
.Face.Selected .Image {
border: 0.25rem solid blue;
}
.Face .Title {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
@ -50,7 +60,9 @@ div {
color: white; color: white;
} }
.Identity .Face { .Face .Image {
position: relative;
box-sizing: border-box;
width: 8rem; width: 8rem;
height: 8rem; height: 8rem;
background-size: contain !important; background-size: contain !important;
@ -58,16 +70,18 @@ div {
background-position: 50% 50% !important;; background-position: 50% 50% !important;;
} }
.Cluster { .Cluster {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow-y: scroll; overflow-y: scroll;
flex-grow: 1; flex-grow: 1;
border: 1px solid red;
padding: 0.5rem; padding: 0.5rem;
} }
.Cluster .Face.Selected {
/* filter: grayscale(100%); */
}
.Cluster .Info { .Cluster .Info {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@ -3,6 +3,21 @@ import React, { useState, useMemo, useEffect } from 'react';
import { useApi } from './useApi'; import { useApi } from './useApi';
import './App.css'; import './App.css';
const Face = ({ faceId, onClick, title }: any) => {
const idPath = String(faceId % 100).padStart(2, '0');
return (
<div onClick={(e) => { onClick(e, faceId) }}
className='Face'>
<div className='Image'
style={{
background: `url("/faces/${idPath}/${faceId}.jpg")`,
}}>
<div className='Title'>{title}</div>
</div>
</div>
);
};
type ClusterProps = { type ClusterProps = {
id: number id: number
}; };
@ -23,48 +38,113 @@ const Cluster = ({ id }: ClusterProps) => {
} }
}, [data]); }, [data]);
const relatedFacesJSX = useMemo(() => { const relatedFacesJSX = useMemo(() => {
const faceClicked = (e: any, id: any) => {
if (!identity) {
return;
}
const el = e.currentTarget;
const face = identity.relatedFaces.find(item => item.faceId === id);
el.classList.toggle('Selected');
console.log(face);
}
if (identity === undefined) { if (identity === undefined) {
return <></>; return <></>;
} }
return identity.relatedFaces.map((face) => { return identity.relatedFaces.map(face =>
const idPath = String(face.faceId % 100).padStart(2, '0'); <Face key={face.faceId}
return ( faceId={face.faceId}
<div key={face.faceId} onClick={faceClicked}
className='Identity'> title={face.distance}/>
<div className='Title'> );
{face.distance}
</div>
<div className='Face'
style={{
background: `url("/faces/${idPath}/${face.faceId}.jpg")`,
}} />
</div>
);
});
}, [identity]); }, [identity]);
const lastNameChanged = (e: any) => {
setIdentity(Object.assign(
{},
identity, {
lastName: e.currentTarget.value
}
));
};
const firstNameChanged = (e: any) => {
setIdentity(Object.assign(
{},
identity, {
firstName: e.currentTarget.value
}
));
};
const middleNameChanged = (e: any) => {
setIdentity(Object.assign(
{},
identity, {
middleName: e.currentTarget.value
}
));
};
const displayNameChanged = (e: any) => {
setIdentity(Object.assign(
{},
identity, {
displayName: e.currentTarget.value
}
));
};
if (loading) {
return (<div className='Cluster'>
{loading && `Loading ${id}...`}
</div>);
}
if (identity === undefined) {
return (<div className='Cluster'>
Select identity to load.
</div>);
}
return ( return (
<div className='Cluster'> <div className='Cluster'>
{ loading && `Loading ${id}`} <div className="Info">
{ identity !== undefined && <div className="Info"> <form>
<div>{identity.lastName}</div> <div><label>Last name:<input type="text"
<div>{identity.firstName}</div> value={identity.lastName}
<div>{identity.middleName}</div> onChange={lastNameChanged}/>
<div>{identity.displayName}</div> </label></div>
<div><label>First name:<input type="text"
value={identity.firstName}
onChange={firstNameChanged} />
</label></div>
<div><label>Middle name:<input type="text"
value={identity.middleName}
onChange={middleNameChanged} />
</label></div>
<div><label>Display name:<input type="text"
value={identity.displayName}
onChange={displayNameChanged} />
</label></div>
<div>Faces: {identity.relatedFaces.length}</div> <div>Faces: {identity.relatedFaces.length}</div>
</div> } </form>
{ identity !== undefined && <div className="Faces"> </div>
<div className="Faces">
{ relatedFacesJSX } { relatedFacesJSX }
</div> } </div>
</div> </div>
); );
}; };
type Face = { type FaceData = {
distance: number,
faceId: number, faceId: number,
photoId: number lastName: string,
firstName: string,
middleName: string,
displayName: string,
identityId: number,
distance: number,
descriptors: any[]
}; };
type Identity = { type Identity = {
@ -74,7 +154,7 @@ type Identity = {
descriptors: number[], descriptors: number[],
id: number id: number
displayName: string, displayName: string,
relatedFaces: Face[] relatedFaces: FaceData[]
}; };
interface IdentitiesProps { interface IdentitiesProps {
@ -83,8 +163,6 @@ interface IdentitiesProps {
}; };
const Identities = ({ identities, setIdentity } : IdentitiesProps) => { const Identities = ({ identities, setIdentity } : IdentitiesProps) => {
const identitiesJSX = useMemo(() => { const identitiesJSX = useMemo(() => {
const loadIdentity = (id: number): void => { const loadIdentity = (id: number): void => {
if (setIdentity) { if (setIdentity) {
@ -93,19 +171,11 @@ const Identities = ({ identities, setIdentity } : IdentitiesProps) => {
}; };
return identities.map((identity) => { return identities.map((identity) => {
const face = identity.relatedFaces[0]; const face = identity.relatedFaces[0];
const idPath = String(face.faceId % 100).padStart(2, '0');
return ( return (
<div key={face.faceId} <Face key={face.faceId}
faceId={face.faceId}
onClick={() => loadIdentity(identity.id)} onClick={() => loadIdentity(identity.id)}
className='Identity'> title={identity.displayName}/>
<div className='Title'>
{identity.displayName}
</div>
<div className='Face'
style={{
background: `url("/faces/${idPath}/${face.faceId}.jpg")`,
}}/>
</div>
); );
}); });
}, [ setIdentity, identities ]); }, [ setIdentity, identities ]);
@ -133,7 +203,7 @@ const App = () => {
return ( return (
<div className="App"> <div className="App">
<div className="Worksheet"> <div className="Worksheet">
{ loading && <div>Loading...</div> } { loading && <div style={{margin:'1rem'}}>Loading...</div> }
{ !loading && identity !== 0 && <Cluster id={identity} />} { !loading && identity !== 0 && <Cluster id={identity} />}
{ !loading && identity === 0 && <div className="Cluster"> { !loading && identity === 0 && <div className="Cluster">
Select identity to edit Select identity to edit

View File

@ -8,31 +8,30 @@ type UseApi = {
}; };
const useApi = (_url: string, _options?: {}) : UseApi => { const useApi = (_url: string, _options?: {}) : UseApi => {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(true);
const [data, setData] = useState(undefined); const [data, setData] = useState(undefined);
const [error, setError] = useState<any>(undefined); const [error, setError] = useState<any>(undefined);
useEffect(() => { useEffect(() => {
if (_url === '' || loading) { if (_url === '') {
return; return;
} }
const fetchApi = async () => { const fetchApi = async () => {
console.log(`Fetching ${_url}...`); console.log(`Fetching ${_url}...`);
setLoading(true);
try { try {
const res = await window.fetch(_url, _options); const res = await window.fetch(_url, _options);
const data = await res.json(); const data = await res.json();
setData(data); setData(data);
setLoading(false);
} catch (e) { } catch (e) {
console.log(e); console.log(e);
setError(e) setError(e)
} finally {
setLoading(false); setLoading(false);
}; };
}; };
fetchApi(); fetchApi();
}, [_url, _options, loading]); }, [_url, _options]);
return { loading, data, error }; return { loading, data, error };
}; };

7
reset-identities.sh Normal file
View File

@ -0,0 +1,7 @@
#!/bin/bash
. .env
cat << EOF | ./query.sh
update faces set identityId=NULL where identityId is not null;
delete from identities where id>0;
delete from sqlite_sequence where name='identities';
EOF