"use strict"; const express = require("express"), Promise = require("bluebird"); let photoDB; require("../db/photos").then(function(db) { photoDB = db; }); const router = express.Router(); router.put('/:id', async (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 } = req.params; if (!id || isNaN(+id)) { return res.status(400).send({ message: `Invalid identity id ${id}` }); } const { displayName, firstName, lastName, middleName } = req.body; if (displayName === undefined || firstName === undefined || lastName === undefined || middleName === undefined) { return res.status(400).send({ message: `Missing fields` }); } await photoDB.sequelize.query( 'UPDATE identities ' + 'SET ' + 'displayName=:displayName, ' + 'firstName=:firstName, ' + 'lastName=:lastName, ' + 'middleName=:middleName ' + 'WHERE id=:id', { replacements: { displayName, firstName, lastName, middleName, id } } ); return res.status(200).json({ displayName, firstName, lastName, middleName, id }); }); 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.`); return res.status(401).send("Unauthorized to modify photos."); } const id = parseInt(req.params.id); if (id != req.params.id) { return res.status(400).send("Invalid identity id."); } if (!Array.isArray(req.body.faces) || req.body.faces.length == 0) { return res.status(400).send("No faces supplied."); } return photoDB.sequelize.query( "UPDATE faces SET identityId=:identityId " + "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("Error processing request."); }); }); router.post("/", (req, res) => { if (!req.user.maintainer) { console.warn(`${req.user.name} attempted to modify photos.`); return res.status(401).send("Unauthorized to modify photos."); } const identity = { lastName: req.body.lastName || "", firstName: req.body.firstName || "", middleName: req.body.middleName || "" }; identity.name = req.body.name || (identity.firstName + " " + identity.lastName); let fields = []; for (let key in identity) { fields.push(key); } if (!Array.isArray(req.body.faces) || req.body.faces.length == 0) { return res.status(400).send("No faces supplied."); } return photoDB.sequelize.query("INSERT INTO identities " + "(" + fields.join(",") + ") " + "VALUES(:" + fields.join(",:") + ")", { replacements: identity, }).then(([ results, metadata ]) => { identity.id = metadata.lastID; return photoDB.sequelize.query( "UPDATE faces SET identityId=:identityId " + "WHERE id IN (:faceIds)", { replacements: { identityId: identity.id, faceIds: req.body.faces } }).then(() => { identity.faces = req.body.faces; return res.status(200).json([identity]); }); }).catch((error) => { console.error(error); return res.status(500).send("Error processing request."); }); }); function bufferToFloat32Array(buffer) { return new Float64Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / Float64Array.BYTES_PER_ELEMENT); } function euclideanDistance(a, b) { let A = bufferToFloat32Array(a); let B = bufferToFloat32Array(b); let sum = 0; for (let i = 0; i < A.length; i++) { let delta = A[i] - B[i]; sum += delta * delta; } return Math.sqrt(sum); } router.get("/:id?", async (req, res) => { console.log(`GET ${req.url}`); let id; if (req.params.id) { id = parseInt(req.params.id); if (id != req.params.id) { return res.status(400).send({ message: "Usage /[id]"}); } } const filter = id ? "WHERE identities.id=:id " : ""; const identities = await photoDB.sequelize.query("SELECT " + "identities.*," + "GROUP_CONCAT(faces.id) AS relatedFaceIds," + "GROUP_CONCAT(faces.descriptorId) AS relatedFaceDescriptorIds," + "GROUP_CONCAT(faces.photoId) AS relatedFacePhotoIds " + "FROM identities " + "INNER JOIN faces ON identities.id=faces.identityId " + filter + "GROUP BY identities.id", { replacements: { id }, type: photoDB.Sequelize.QueryTypes.SELECT, raw: true }); await Promise.map(identities, async (identity) => { [ 'firstName', 'middleName', 'lastName' ].forEach(key => { if (!identity[key]) { identity[key] = ''; } }); identity.identityId = identity.id; const relatedFaces = identity.relatedFaceIds.split(","), relatedFacePhotos = identity.relatedFacePhotoIds.split(","); let descriptors = await photoDB.sequelize.query( `SELECT descriptors FROM facedescriptors WHERE id in (:ids)`, { replacements: { ids: identity.relatedFaceDescriptorIds.split(',') }, type: photoDB.Sequelize.QueryTypes.SELECT, raw: true } ); descriptors = descriptors.map(entry => entry.descriptors); identity.relatedFaces = relatedFaces.map((faceId, index) => { const distance = euclideanDistance( descriptors[index], identity.descriptors ); return { identityId: identity.id, faceId, photoId: relatedFacePhotos[index], distance }; }); identity .relatedFaces .sort((A, B) => { return A.distance - B.distance; }); /* If no filter was specified, only return the best face for * the identity */ if (!filter) { identity.relatedFaces = [ identity.relatedFaces[0] ]; } delete identity.id; delete identity.descriptors; delete identity.relatedFaceIds; delete identity.relatedFacePhotoIds; delete identity.relatedFaceDescriptorIds; delete identity.relatedIdentityDescriptors; }); return res.status(200).json(identities); }); module.exports = router;