diff --git a/frontend/src/common/config.js b/frontend/src/common/config.js index b87e36c65..383985fed 100644 --- a/frontend/src/common/config.js +++ b/frontend/src/common/config.js @@ -146,6 +146,11 @@ export default class Config { this.values.people = []; } + if (!data || !data.entities) { + console.warn("empty event data", ev, data); + return; + } + switch (type) { case "created": this.values.people.unshift(...data.entities); diff --git a/frontend/src/component/components.js b/frontend/src/component/components.js index 6505e543c..b4e25b74f 100644 --- a/frontend/src/component/components.js +++ b/frontend/src/component/components.js @@ -43,6 +43,7 @@ import PAlbumClipboard from "./album/clipboard.vue"; import PAlbumToolbar from "./album/toolbar.vue"; import PLabelClipboard from "./label/clipboard.vue"; import PFileClipboard from "./file/clipboard.vue"; +import PSubjectClipboard from "./subject/clipboard.vue"; import PAboutFooter from "./footer.vue"; const components = {}; @@ -63,6 +64,7 @@ components.install = (Vue) => { Vue.component("PAlbumToolbar", PAlbumToolbar); Vue.component("PLabelClipboard", PLabelClipboard); Vue.component("PFileClipboard", PFileClipboard); + Vue.component("PSubjectClipboard", PSubjectClipboard); Vue.component("PAboutFooter", PAboutFooter); }; diff --git a/frontend/src/component/navigation.vue b/frontend/src/component/navigation.vue index 54a181cda..707ff18fe 100644 --- a/frontend/src/component/navigation.vue +++ b/frontend/src/component/navigation.vue @@ -223,6 +223,20 @@ + + + person + + + + + People + {{ config.count.people }} + + + + diff --git a/frontend/src/component/subject/clipboard.vue b/frontend/src/component/subject/clipboard.vue new file mode 100644 index 000000000..81a9e7ac0 --- /dev/null +++ b/frontend/src/component/subject/clipboard.vue @@ -0,0 +1,121 @@ + + + + + + + menu + {{ selection.length }} + + + + + get_app + + + + bookmark + + + + clear + + + + + + + diff --git a/frontend/src/dialog/photo/people.vue b/frontend/src/dialog/photo/people.vue index 8aae00a10..3e7c37ddc 100644 --- a/frontend/src/dialog/photo/people.vue +++ b/frontend/src/dialog/photo/people.vue @@ -29,7 +29,7 @@ :transition="false" aspect-ratio="1" class="accent lighten-2"> - clear @@ -43,11 +43,11 @@ large depressed block :round="false" class="action-approve text-xs-center" :title="$gettext('Approve')" @click.stop="onApprove(marker)"> - check + undo - + Promise.resolve(this.setValues(resp.data)) diff --git a/frontend/src/pages/people.vue b/frontend/src/pages/people.vue deleted file mode 100644 index 98c08d58f..000000000 --- a/frontend/src/pages/people.vue +++ /dev/null @@ -1,31 +0,0 @@ - - - - - Not implemented yet - - - - - - - - Issues labeled help wanted / - easy can be good (first) - contributions. - Our Developer Guide contains all information - necessary to get you started. - - - - - - diff --git a/frontend/src/pages/photos.vue b/frontend/src/pages/photos.vue index 0595368a3..066cc904f 100644 --- a/frontend/src/pages/photos.vue +++ b/frontend/src/pages/photos.vue @@ -163,7 +163,7 @@ export default { }, methods: { viewType() { - let queryParam = this.$route.query['view']; + let queryParam = this.$route.query["view"]; let storedType = window.localStorage.getItem("photo_view"); if (queryParam) { @@ -178,7 +178,7 @@ export default { return 'cards'; }, sortOrder() { - let queryParam = this.$route.query['order']; + let queryParam = this.$route.query["order"]; let storedType = window.localStorage.getItem("photo_order"); if (queryParam) { @@ -188,7 +188,7 @@ export default { return storedType; } - return 'newest'; + return "newest"; }, openLocation(index) { const photo = this.results[index]; diff --git a/frontend/src/pages/subjects.vue b/frontend/src/pages/subjects.vue new file mode 100644 index 000000000..02d6a810e --- /dev/null +++ b/frontend/src/pages/subjects.vue @@ -0,0 +1,555 @@ + + + + + + + + + + + refresh + + + + + + + + + + + + + + + + + + Couldn't find anything + + + Try again using other filters or keywords. + + + + + + + + + + + check_circle + radio_button_off + + + + star + star_border + + + + + + + {{ model.Name }} + + + edit + + + + + + + + + + {{ model.Bio | truncate(100) }} + + + + + Contains one entry. + + + Contains %{n} entries. + + + + + + + + + + + + diff --git a/frontend/src/routes.js b/frontend/src/routes.js index 4f48bbb17..133c6848d 100644 --- a/frontend/src/routes.js +++ b/frontend/src/routes.js @@ -35,7 +35,7 @@ import Places from "pages/places.vue"; import Files from "pages/library/files.vue"; import Errors from "pages/library/errors.vue"; import Labels from "pages/labels.vue"; -import People from "pages/people.vue"; +import Subjects from "pages/subjects.vue"; import Library from "pages/library.vue"; import Settings from "pages/settings.vue"; import Login from "pages/login.vue"; @@ -261,7 +261,7 @@ export default [ { name: "people", path: "/people", - component: People, + component: Subjects, meta: { title: $gettext("People"), auth: true }, }, { diff --git a/internal/api/event.go b/internal/api/event.go index 29f5cc098..3fd3439d2 100644 --- a/internal/api/event.go +++ b/internal/api/event.go @@ -53,3 +53,16 @@ func PublishLabelEvent(e EntityEvent, uid string, c *gin.Context) { event.PublishEntities("labels", string(e), result) } + +func PublishSubjectEvent(e EntityEvent, uid string, c *gin.Context) { + f := form.SubjectSearch{ID: uid} + result, err := query.SubjectSearch(f) + + if err != nil { + log.Error(err) + AbortUnexpected(c) + return + } + + event.PublishEntities("subjects", string(e), result) +} diff --git a/internal/api/label.go b/internal/api/label.go index fc1571952..a1c81a489 100644 --- a/internal/api/label.go +++ b/internal/api/label.go @@ -14,6 +14,8 @@ import ( "github.com/photoprism/photoprism/pkg/txt" ) +// GetLabels finds and returns labels as JSON. +// // GET /api/v1/labels func GetLabels(router *gin.RouterGroup) { router.GET("/labels", func(c *gin.Context) { @@ -49,6 +51,8 @@ func GetLabels(router *gin.RouterGroup) { }) } +// UpdateLabel updates label properties. +// // PUT /api/v1/labels/:uid func UpdateLabel(router *gin.RouterGroup) { router.PUT("/labels/:uid", func(c *gin.Context) { @@ -85,6 +89,8 @@ func UpdateLabel(router *gin.RouterGroup) { }) } +// LikeLabel flags a label as favorite. +// // POST /api/v1/labels/:uid/like // // Parameters: @@ -123,6 +129,8 @@ func LikeLabel(router *gin.RouterGroup) { }) } +// DislikeLabel removes the favorite flag from a label. +// // DELETE /api/v1/labels/:uid/like // // Parameters: diff --git a/internal/api/marker.go b/internal/api/marker.go index f5ce2cd88..3b87cbc3d 100644 --- a/internal/api/marker.go +++ b/internal/api/marker.go @@ -93,7 +93,7 @@ func UpdateMarker(router *gin.RouterGroup) { log.Errorf("photo: %s (save marker form)", err) AbortSaveFailed(c) return - } else if marker.SubjectUID != "" && marker.SubjectSrc == entity.SrcManual && marker.FaceID != "" { + } else if marker.SubjUID != "" && marker.SubjSrc == entity.SrcManual && marker.FaceID != "" { if res, err := service.Faces().Optimize(); err != nil { log.Errorf("faces: %s (optimize)", err) } else if res.Merged > 0 { diff --git a/internal/api/marker_test.go b/internal/api/marker_test.go index 3e122a42d..b34eaab33 100644 --- a/internal/api/marker_test.go +++ b/internal/api/marker_test.go @@ -35,7 +35,7 @@ func TestUpdateMarker(t *testing.T) { u := fmt.Sprintf("/api/v1/markers/%s", markerUID) var m = form.Marker{ - SubjectSrc: "manual", + SubjSrc: "manual", MarkerInvalid: true, MarkerName: "Foo", } @@ -100,7 +100,7 @@ func TestUpdateMarker(t *testing.T) { UpdateMarker(router) var m = form.Marker{ - SubjectSrc: "manual", + SubjSrc: "manual", MarkerInvalid: false, MarkerName: "Actress A", } @@ -125,7 +125,7 @@ func TestUpdateMarker(t *testing.T) { UpdateMarker(router) var m = form.Marker{ - SubjectSrc: "manual", + SubjSrc: "manual", MarkerInvalid: false, MarkerName: "Actress A", } @@ -150,20 +150,20 @@ func TestUpdateMarker(t *testing.T) { UpdateMarker(router) var m = struct { - ID int - Type string - Src int - Name int - SubjectUID string - SubjectSrc string - FaceID string + ID int + Type string + Src int + Name int + SubjUID string + SubjSrc string + FaceID string }{ID: 8, - Type: "face", - Src: 123, - Name: 456, - SubjectUID: "jqy1y111h1njaaac", - SubjectSrc: "manual", - FaceID: "GMH5NISEEULNJL6RATITOA3TMZXMTMCI"} + Type: "face", + Src: 123, + Name: 456, + SubjUID: "jqy1y111h1njaaac", + SubjSrc: "manual", + FaceID: "GMH5NISEEULNJL6RATITOA3TMZXMTMCI"} if b, err := json.Marshal(m); err != nil { t.Fatal(err) } else { diff --git a/internal/api/subject.go b/internal/api/subject.go index f5d38e4a5..5295cfc40 100644 --- a/internal/api/subject.go +++ b/internal/api/subject.go @@ -7,6 +7,7 @@ import ( "github.com/gin-gonic/gin/binding" "github.com/photoprism/photoprism/internal/acl" "github.com/photoprism/photoprism/internal/entity" + "github.com/photoprism/photoprism/internal/event" "github.com/photoprism/photoprism/internal/form" "github.com/photoprism/photoprism/internal/i18n" "github.com/photoprism/photoprism/internal/query" @@ -68,6 +69,111 @@ func GetSubject(router *gin.RouterGroup) { } else { c.JSON(http.StatusOK, subj) } - + }) +} + +// UpdateSubject updates subject properties. +// +// PUT /api/v1/subjects/:uid +func UpdateSubject(router *gin.RouterGroup) { + router.PUT("/subjects/:uid", func(c *gin.Context) { + s := Auth(SessionID(c), acl.ResourceSubjects, acl.ActionUpdate) + + if s.Invalid() { + AbortUnauthorized(c) + return + } + + var f form.Subject + + if err := c.BindJSON(&f); err != nil { + AbortBadRequest(c) + return + } + + uid := c.Param("uid") + m := entity.FindSubject(uid) + + if m == nil { + Abort(c, http.StatusNotFound, i18n.ErrSubjectNotFound) + return + } + + if _, err := m.UpdateName(f.SubjName); err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UcFirst(err.Error())}) + return + } + + event.SuccessMsg(i18n.MsgSubjectSaved) + + c.JSON(http.StatusOK, m) + }) +} + +// LikeSubject flags a subject as favorite. +// +// POST /api/v1/subjects/:uid/like +// +// Parameters: +// uid: string Subject UID +func LikeSubject(router *gin.RouterGroup) { + router.POST("/subjects/:uid/like", func(c *gin.Context) { + s := Auth(SessionID(c), acl.ResourceSubjects, acl.ActionUpdate) + + if s.Invalid() { + AbortUnauthorized(c) + return + } + + uid := c.Param("uid") + subj := entity.FindSubject(uid) + + if subj == nil { + Abort(c, http.StatusNotFound, i18n.ErrSubjectNotFound) + return + } + + if err := subj.Update("SubjFavorite", true); err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UcFirst(err.Error())}) + return + } + + PublishSubjectEvent(EntityUpdated, uid, c) + + c.JSON(http.StatusOK, http.Response{}) + }) +} + +// DislikeSubject removes the favorite flag from a subject. +// +// DELETE /api/v1/subjects/:uid/like +// +// Parameters: +// uid: string Subject UID +func DislikeSubject(router *gin.RouterGroup) { + router.DELETE("/subjects/:uid/like", func(c *gin.Context) { + s := Auth(SessionID(c), acl.ResourceSubjects, acl.ActionUpdate) + + if s.Invalid() { + AbortUnauthorized(c) + return + } + + uid := c.Param("uid") + subj := entity.FindSubject(uid) + + if subj == nil { + Abort(c, http.StatusNotFound, i18n.ErrSubjectNotFound) + return + } + + if err := subj.Update("SubjFavorite", false); err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UcFirst(err.Error())}) + return + } + + PublishSubjectEvent(EntityUpdated, uid, c) + + c.JSON(http.StatusOK, http.Response{}) }) } diff --git a/internal/entity/deprecated.go b/internal/entity/deprecated.go index 45cb3c83e..4cb1c6e1d 100644 --- a/internal/entity/deprecated.go +++ b/internal/entity/deprecated.go @@ -21,6 +21,7 @@ var DeprecatedTables = Deprecated{ "subjects_dev5", "subjects_dev6", "subjects_dev7", + "subjects_dev8", "markers_dev1", "markers_dev2", "markers_dev3", @@ -28,6 +29,7 @@ var DeprecatedTables = Deprecated{ "markers_dev5", "markers_dev6", "markers_dev7", + "markers_dev8", "faces_dev1", "faces_dev2", "faces_dev3", @@ -35,4 +37,5 @@ var DeprecatedTables = Deprecated{ "faces_dev5", "faces_dev6", "faces_dev7", + "faces_dev8", } diff --git a/internal/entity/face.go b/internal/entity/face.go index faf2559f3..f52024a73 100644 --- a/internal/entity/face.go +++ b/internal/entity/face.go @@ -21,7 +21,7 @@ var faceMutex = sync.Mutex{} type Face struct { ID string `gorm:"type:VARBINARY(42);primary_key;auto_increment:false;" json:"ID" yaml:"ID"` FaceSrc string `gorm:"type:VARBINARY(8);" json:"Src" yaml:"Src,omitempty"` - SubjectUID string `gorm:"type:VARBINARY(42);index;" json:"SubjectUID" yaml:"SubjectUID,omitempty"` + SubjUID string `gorm:"type:VARBINARY(42);index;" json:"SubjUID" yaml:"SubjUID,omitempty"` Samples int `json:"Samples" yaml:"Samples,omitempty"` SampleRadius float64 `json:"SampleRadius" yaml:"SampleRadius,omitempty"` Collisions int `json:"Collisions" yaml:"Collisions,omitempty"` @@ -38,14 +38,14 @@ var Faceless = []string{""} // TableName returns the entity database table name. func (Face) TableName() string { - return "faces_dev8" + return "faces_dev9" } // NewFace returns a new face. -func NewFace(subjectUID, faceSrc string, embeddings Embeddings) *Face { +func NewFace(subjUID, faceSrc string, embeddings Embeddings) *Face { result := &Face{ - SubjectUID: subjectUID, - FaceSrc: faceSrc, + SubjUID: subjUID, + FaceSrc: faceSrc, } if err := result.SetEmbeddings(embeddings); err != nil { @@ -145,7 +145,7 @@ func (m *Face) Match(embeddings Embeddings) (match bool, dist float64) { // ResolveCollision resolves a collision with a different subject's face. func (m *Face) ResolveCollision(embeddings Embeddings) (resolved bool, err error) { - if m.SubjectUID == "" { + if m.SubjUID == "" { // Ignore reports for anonymous faces. return false, nil } else if m.ID == "" { @@ -165,9 +165,9 @@ func (m *Face) ResolveCollision(embeddings Embeddings) (resolved bool, err error log.Infof("faces: %s collision at dist %f reported, same person?", m.ID, dist) // Reset subject UID just in case. - m.SubjectUID = "" + m.SubjUID = "" - return false, m.Updates(Values{"SubjectUID": m.SubjectUID}) + return false, m.Updates(Values{"SubjUID": m.SubjUID}) } else { m.MatchedAt = nil m.Collisions++ @@ -242,20 +242,20 @@ func (m *Face) MatchMarkers(faceIds []string) error { } // SetSubjectUID updates the face's subject uid and related markers. -func (m *Face) SetSubjectUID(uid string) (err error) { +func (m *Face) SetSubjectUID(subjUID string) (err error) { // Update face. - if err = m.Update("SubjectUID", uid); err != nil { + if err = m.Update("SubjUID", subjUID); err != nil { return err } else { - m.SubjectUID = uid + m.SubjUID = subjUID } // Update related markers. if err = Db().Model(&Marker{}). Where("face_id = ?", m.ID). - Where("subject_src = ?", SrcAuto). - Where("subject_uid <> ?", m.SubjectUID). - Updates(Values{"SubjectUID": m.SubjectUID, "Review": false}).Error; err != nil { + Where("subj_src = ?", SrcAuto). + Where("subj_uid <> ?", m.SubjUID). + Updates(Values{"SubjUID": m.SubjUID, "MarkerReview": false}).Error; err != nil { return err } @@ -309,12 +309,12 @@ func FirstOrCreateFace(m *Face) *Face { result := Face{} if err := UnscopedDb().Where("id = ?", m.ID).First(&result).Error; err == nil { - log.Warnf("faces: %s has ambiguous subject %s", m.ID, m.SubjectUID) + log.Warnf("faces: %s has ambiguous subject %s", m.ID, m.SubjUID) return &result } else if createErr := m.Create(); createErr == nil { return m } else if err := UnscopedDb().Where("id = ?", m.ID).First(&result).Error; err == nil { - log.Warnf("faces: %s has ambiguous subject %s", m.ID, m.SubjectUID) + log.Warnf("faces: %s has ambiguous subject %s", m.ID, m.SubjUID) return &result } else { log.Errorf("faces: %s when trying to create %s", createErr, m.ID) diff --git a/internal/entity/face_fixtures.go b/internal/entity/face_fixtures.go index fd43555b4..214d37b96 100644 --- a/internal/entity/face_fixtures.go +++ b/internal/entity/face_fixtures.go @@ -5,7 +5,7 @@ var UnknownFace = Face{ ID: UnknownID, FaceSrc: SrcDefault, MatchedAt: TimePointer(), - SubjectUID: "", + SubjUID: "", EmbeddingJSON: []byte{}, } @@ -31,7 +31,7 @@ var FaceFixtures = FaceMap{ "john-doe": Face{ ID: "PN6QO5INYTUSAATOFL43LL2ABAV5ACZK", EmbeddingJSON: []byte("[-0.015105677000169373,-0.0005582744874266051,0.0480268501406662,-0.03601115760977173,0.0005837739331054689,0.014168089953522492,-0.03945336629091645,-0.02582770810183487,0.020898761370506287,0.049166841007919314,0.021960500727076723,-0.019064476425213243,0.02198730232398834,0.0006670523258575441,0.03028046368291855,0.00649638007861023,-0.01584042613295212,0.00530397326763153,-0.06404373233350608,0.03889477750802231,-0.0365899261901268,0.0360787547656456,0.07579429823342132,0.039115246617597965,-0.0777424514863777,0.035950400633987425,-0.01904672616637268,0.031240118709612276,-0.01142303718094612,0.021169409966093445,-0.04066713893357315,0.048964979162612915,-0.016256413336937715,-0.02378354035888214,-0.020449917192553707,-0.02040227800516968,0.06541690078029251,0.016761249534914705,0.03380005035063598,-0.10038679582336427,0.043679719882033535,0.03128701222455444,0.012390233834701538,0.05524386205410767,0.021537026348385238,-0.016655772903281404,0.007295974349593354,-0.000005785312992858532,-0.047790829845092773,0.017440576051146697,-0.06534523391118621,0.06609203363401413,0.03238222320728302,0.006614993631034087,-0.054927458582173155,0.030833110256991578,-0.0056855157837505345,-0.007370784518985901,0.0013351148462055202,0.047366844577483365,0.04135019693103943,-0.02510304556774292,-0.05305447016115189,-0.03180977055114746,0.012452435193779296,-0.031038226814250947,-0.06159064720963287,0.057565623878417974,0.006987256155133055,0.10980759767712402,0.0014624758511650082,0.04941792959784012,0.02249399537556839,-0.007318808567618561,0.011643714617645264,-0.033996864613095854,-0.10756568994960479,-0.008790611639031982,-0.06265860528228379,-0.03834362793397904,-0.007061803256170654,-0.02360514984864273,-0.030340688408576965,-0.006711280069554138,-0.026063804222563172,-0.06257436487065124,-0.03489222804085922,0.023775160926244356,-0.05953325934691238,-0.0015599671627334586,0.04257923482005539,-0.02072714042014847,0.042186514202873227,0.03334131403590164,-0.04231802868860626,-0.03938228046069336,0.04991599600813751,0.005012853217630309,0.02852926982268677,-0.04592934168044281,-0.0446876756071167,0.05780069138760376,-0.06523845092017364,0.03691154157677917,0.0048527119732078555,-0.008498008999919891,-0.06625776342607116,0.013960302090915604,-0.029253019821994783,0.02477269871644745,0.010155390116101837,0.03100301871340158,-0.07413353268207168,0.01750142506529999,0.03461705135130691,0.04982243958808899,0.00851692480498886,-0.002991416728403473,0.01759018484255066,-0.07281463888115311,0.07669263036101531,0.0287119138326683,-0.0011225693401481635,-0.022214332482351686,0.08318748801869094,0.06656315672954559,0.0034995880579914086,0.04958693578422928,-0.027691547638692474,-0.03405751975708465,-0.062014044191894525,-0.0004367024225837711,0.007944679561768342,0.03339219333856888,-0.04039564807562256,-0.06730881179145813,-0.07099339792601776,-0.033052287483390806,-0.050682249608245855,0.011561693050074387,-0.09867453691846315,-0.02774825273226944,0.010811723733195496,0.02427582366474533,-0.02078586645137596,0.01433492286357048,0.026900254564299775,-0.05306856266233825,-0.007102414552368164,-0.02275204349867813,-0.03211197205357055,-0.014967170406674196,0.022953155306690905,-0.03644228327174301,0.012341418549354555,0.020687427985451052,-0.09076456366364288,0.031082595049208066,0.04102866745965958,-0.02306090883122635,0.027812227150472258,-0.013119295590370941,-0.004158771480469893,-0.007025444076653517,0.009275999232177735,-0.039368103392872236,0.01910143328294525,-0.05630273885418091,0.055353015055413055,-0.0014070097039474495,0.042947509516956334,0.0024906970417621615,-0.03561797166536834,0.006505803784743882,0.006665486497595978,-0.006378142944042969,0.05448368035679932,0.07332471840206146,-0.0014946864171386718,-0.008414893981474686,-0.00006825545633163364,0.02668107540572357,-0.024539537215417483,-0.07293688736830901,0.06344162595002364,-0.033607367797969814,-0.01986442914540863,0.026089177944317626,-0.022147074482840423,-0.08048881567908478,0.050637503425783534,-0.06346700348937226,-0.015700777833213043,-0.04264569287159348,-0.002655311902906036,-0.03654008229608307,-0.007810925734401397,-0.0435794361814415,-0.011699671276438905,0.057055175916534424,0.04544679800120926,0.037604382557088464,-0.02941164037058258,-0.05797836845658112,-0.06812897444572831,0.08032934278161621,-0.01328043643980198,-0.08971531949739076,0.0065821178142807,0.008762343139662933,0.020370728524544524,0.03974483299145508,-0.0051898651247510525,-0.0031604766306224056,0.07640609197084428,0.038082580676622776,0.05806105880397415,-0.014124205852999725,0.007738546446239335,-0.04230980231715012,-0.01059650656994934,0.010447661136037827,-0.04688999712561989,-0.06730292583744431,0.016754640387374113,-0.011607031355502319,0.023310211609113313,0.08731236136631013,0.05903231309516144,0.04731763803346557,-0.02573253768891525,-0.06296218933005523,0.04012439522837066,0.025853857002137298,0.007843758596900636,0.046765933136711124,-0.0018710897452026364,-0.005570995783544921,0.0005954957864215851,0.029306860778232576,0.03074633266388397,0.02318916068950821,-0.011157762649505614,-0.013694216304312515,0.015094815793841173,-0.008802384161989595,0.032486344160458376,0.05804673958212662,-0.006112833595197296,0.006215988022933197,0.04096582699106979,-0.03384389014125061,-0.01596561475662651,-0.010684742677253074,0.07758476984541932,-0.005856569876429291,0.024524756417617796,-0.007312638971189117,-0.017082938609609223,0.0011795664558563231,0.0134139216274601,-0.09028529851032258,-0.039419858379217534,0.022968888672964094,0.004521465961741944,-0.06451345260910797,-0.04178954661956787,-0.024166587057536396,0.012531514465320588,0.02323170220531845,0.04000117979464722,0.03214717679358063,0.056948825253341676,0.05539476134574128,0.013257249967432405,-0.0001733097061775205,-0.01234630922813568,0.0645579352090454,0.029838023576596068,-0.06455350567951279,-0.013353814450276946,0.0018903859134303275,0.0483956355803524,-0.004311148732519531,-0.014597871326852417,-0.006853384533165969,0.04057120248826332,0.007104495398272705,-0.027431613068233492,-0.09228961372112274,-0.06612821609320907,0.0607875549739154,-0.026342729023012542,0.06454961398086471,0.004993419094538574,-0.031680751000938416,0.007030828889411162,-0.010022039687029268,-0.05428986021428299,0.017565788880432403,0.04025441392524147,0.009023673541622163,0.009042981797892759,0.019972970522025298,0.058956133531005865,-0.008320068883990478,0.02986469039841843,-0.053139615835475926,-0.024647880182499998,0.007398424838289642,0.09050773480216218,-0.015264475845877838,-0.06385353173209762,0.016500261584825135,0.02948610469222336,-0.02504260549529915,-0.018835162326039122,0.050848700451684575,-0.03199441179409637,-0.019780421813622284,-0.027626615849293518,-0.03419102001171112,-0.014344497819357758,0.07330804638220215,-0.02937739203966522,-0.00813064343063736,-0.01626787766690979,-0.011862281442715148,0.01708495452203865,-0.07908452169694902,0.023940025203119657,-0.13222904368403626,0.048117978374713134,-0.017696039928765107,0.004759198936071777,0.01974903359319687,0.010760925580199815,-0.005599215416766357,0.0457550643199234,-0.01929695170711136,-0.007629453917987061,-0.010118543879554747,0.015504727550686645,0.03245994598398285,0.05049195915774918,-0.06673184178328552,-0.01761782965561676,0.008011984665633392,0.023189021348875426,0.06720727829968262,0.010742643820915984,-0.03080342023261017,-0.050128828091691585,0.04935064662135315,0.012293871085033494,0.05639020959568253,-0.02957390940026932,-0.03676535286145477,-0.004796114565882111,0.05806600428461456,0.018004375270679475,-0.020827009949742126,-0.0336628292589238,-0.0018759192028083797,-0.06518067211773682,-0.02731396445179306,-0.03430377823384857,0.016512887052202073,0.061277289867412564,-0.019134398406000827,0.05822277240970993,0.044241398569797516,-0.005043084829421997,-0.04018607674626312,0.06927013858769607,-0.004344519528141023,0.012868128092136383,-0.08525498011501847,0.03454513801989975,-0.024010073233037568,0.02195751011467056,-0.006334513400955963,0.06579357440817261,-0.009711559918811036,-0.013345796425511931,0.014702989589321902,0.025344240384365696,0.021528159041582107,0.062108358473124695,-0.05624000855096435,0.05573243638398361,0.006079895555899353,-0.002001432319259644,-0.002443507762801743,-0.010721616536909867,-0.04295436979775849,0.08180931740979766,-0.014715828856610106,-0.03683209432014542,-0.012104101579055901,0.013437134275827486,0.06964065576705855,0.06482510509484864,-0.03570993432865906,-0.05314338450149346,-0.11395284172808075,0.01426200422902832,0.012089334595506287,-0.058501489031186675,0.060430661880981444,0.0500264671381279,0.056157483018959044,-0.05001220665976715,0.07527209592948914,0.031277665487422945,-0.02043782967053299,-0.08342602471109009,-0.0008383195362091063,0.045666852330976865,-0.05341016683117676,-0.0003998352299102786,-0.03577085955021919,-0.038460902215493774,-0.01896648460201416,0.012595410857717131,-0.0060184891112971504,0.019921306564183502,-0.060824947492757256,-0.08852370824755859,0.003392263133862915,0.0036400299628715506,0.03255903972799225,-0.008351102953604507,0.01567778415994263,-0.00023129789984893678,-0.023584983993255156,0.06815277690185165,0.032341226983652875,0.0026074800379161833,-0.0395687710902481,-0.003838149411266327,0.04100280903240967,-0.019130064566046143,-0.07410366493117905,-0.07626375565908813,-0.014898051247527312,-0.02028174410337219,-0.0413983021689148,0.05420100971967316,0.02752598950049637,-0.024227222049773407,0.01466367372080475,0.013159825923877717,0.03946128732824249,0.02427177671503067,0.03591388100347939,-0.003222521540391235,0.0183274046263958,-0.03000545738402557,-0.04066711644104462,0.017155749921449585,-0.06134375247813416,-0.036362408407546995,-0.019471377535612868,0.034648935124684144,0.01776573165824356,-0.005382945029614257,-0.034531315796222685,-0.004955845186902466,-0.023998231859699858,0.03130629873801392,-0.012495260702575683,-0.031263154234012984,-0.063910194205719,0.01745994660978073,0.038960702542871095,0.04021203330704117,0.023016385340854643,-0.0403753269719162,-0.018214478771432493,0.009230925650691108,0.031802766191501616,-0.0060094809595568855,-0.05665428473038101,-0.0026934481642271802,-0.01278887994417572,0.013757778161637,-0.06991252187433472,0.025432326767399597,-0.05928275511783676,-0.0071204498857164004,0.009102611771803285,-0.048021745720825196,-0.04234071017774048,0.0378105065918541,-0.0030949641070522308,-0.0802945368397522,-0.029345935146197513,-0.055173430818717964,0.035613126717409516,-0.07929294203311157,0.011167600883222962,-0.010140670248493196,0.018876611506684494,0.038331590555036925,-0.021097202962330627,-0.02206313974373703,-0.0409461827742096,0.07130217153391571,0.06408282878469848,0.005774317041436768,-0.07263691064697265]"), - SubjectUID: SubjectFixtures.Get("john-doe").SubjectUID, + SubjUID: SubjectFixtures.Get("john-doe").SubjUID, FaceSrc: SrcAuto, SampleRadius: 0.8, Samples: 5, @@ -42,7 +42,7 @@ var FaceFixtures = FaceMap{ "unknown": Face{ ID: "IW2P73ISBCUFPIAWSIOZKRDCHHFHC35S", EmbeddingJSON: []byte("[-0.01391313475,-0.031814132,0.017377053075,-0.01851261325,0.021255451499999998,-0.051055306,0.0366150855,0.007998470175,0.0695672645,-0.1067636275,0.0307072165,0.02941578425,0.054768010500000006,0.041151049,-0.06493903375,0.0015896528499999996,-0.05437162425,0.011924136,-0.025054364,0.0009921848250000003,0.012807875,-0.01319641825,-0.00488935855,-0.07366566999999999,-0.0611034065,-0.06666547924999999,-0.04888238425,-0.035091203,-0.01854779615,-0.024557007999999998,0.0030882324,-0.055875238499999993,-0.0246700375,-0.02172630285,-0.025565405,0.058197003,0.0547107775,0.047857131,-0.04580487,-0.00021249714999999926,0.009770386499999999,0.044646309999999995,0.0018705221250000003,0.0371642205,0.018666550325,-0.0032051121500000002,0.0057689690000000005,0.06864431525,-0.04906746575,-0.07431345875,-0.0647896445,0.0120775261,-0.02890944,0.01879340475,0.024201165499999996,0.04355076025,-0.014099167999999999,0.026033113750000003,0.025052837749999998,-0.068487575,0.022659832499999998,-0.0027816580000000002,0.03500908899999999,0.012073708,0.006179701499999999,0.034802054,-0.01799351825,0.0457090165,0.0036213202500000002,-0.008580024424999999,-0.072938294,-0.016224131250000003,0.06459961750000001,-0.04963675325,0.08387918625,-0.13179572750000002,-0.011209804,0.011490459325,-0.04273579375,-0.0603162555,-0.03941750125,0.061718414,0.029566903,0.011713920749999999,-0.07820421,0.042141458,0.04053815075,-0.0431640475,-0.009912542250000001,0.0083520422,0.04968228825,0.061740414,-0.003251780325,-0.0009849047250000001,-0.07888640699999999,0.00013775579999999977,0.0545293975,0.0314430285,-0.025010752249999997,-0.0754261725,0.00026732155,-0.04167998875,-0.038423044,-0.012128476325,-0.042172063999999995,-0.03992470075,0.055492729500000004,0.005174035799999999,-0.0491557875,0.047224008,0.027870127375,0.11123915125,-0.0017566490000000008,0.06637293625,0.08444959,-0.038633152000000004,0.040549324,0.0181765085,-0.0103707255,-0.052444608749999996,-0.0721162075,0.0017395296250000006,0.03576854825,-0.0010544938500000002,-0.03536455575,0.026296009000000002,-0.011038092,0.009561795625000001,0.0061376165,0.016413343999999996,-0.0007041408250000004,0.008430746000000001,0.007772767000000001,0.035255850500000005,0.03609753,-0.004700959499999999,-0.0244996355,0.029143511,0.0019076424499999993,-0.02425797625,-0.02824462725,0.03748850525,0.004536174249999999,0.094921252,0.0402888305,-0.035919796500000004,0.0325914395,0.028936399249999998,0.0100435923175,0.051378176750000004,0.0466342265,-0.104555035,-0.058734387250000006,-0.053190730000000006,-0.004201957500000001,-0.0030598753299999996,-0.009583216875000002,-0.05078611825,-0.037133980925000004,0.0406709395,0.01118452775,-0.028617762,-0.006732865225,-0.03122189575,-0.054440001749999994,0.055957135,-0.054629830500000004,0.00548484575,0.0018623927500000005,-0.0054835023,0.04359883825,-0.04169836625,0.025491153,0.035283773500000004,0.020951236874999998,0.017083047,0.011288541499999999,0.029325848999999998,0.031001949125000004,0.04632318125,0.058114136999999996,-0.018353527600000002,0.0010329377499999999,-0.05167622725,0.0015087445249999999,0.0665028595,-0.017552406,0.0030785925075000006,0.067237884,-0.045436311,0.022067097,-0.068492519,-0.09581154575,0.02946200275,0.019267693750000002,-0.013623341,-0.0023418241499999996,0.057798097500000006,-0.02830513875,0.00967313775,-0.0156008155,-0.038696254,-0.046455728749999994,-0.020938623415,0.00489014375,0.0191956194,0.01110382618,0.020775547499999998,0.116135215,-0.040037983750000006,0.039333228250000005,0.011678268775,0.0650113585,0.0023590035,-0.05381264350000001,-0.008376887255000002,-0.05688126,0.01151456675,0.00003846570000000035,-0.01287236325,-0.06661450099999999,-0.03827642075,-0.045446761,-0.03154112575,0.028140978,0.070225409,-0.020004859,0.0193362475,-0.0034820117499999997,-0.074293175,-0.04876132,0.0074359382499999994,0.0066796961250000005,-0.011634815875,0.009048144557499999,0.0344562235,-0.01762691475,0.019571052749999998,0.026697968750000002,0.015484052000000002,0.03876590725,0.0037216567,0.04729691375,0.008930467375,-0.07800198875,-0.043134631,0.0424968855,-0.0057933688750000005,-0.075971065,-0.005398360425,0.01736323075,-0.03068225125,-0.0700152155,0.012127145199999998,0.052686005499999994,0.00609444565,-0.05195201175,-0.01496918725,0.032330921,-0.0251999175,-0.0317094965,0.0063436068250000005,-0.046775514000000004,0.024125194250000002,-0.025942028000000002,0.0683850275,0.048410787250000004,-0.020441745875,0.0014374775000000001,-0.007761475925,-0.003657494249999999,-0.01002698675,-0.0033783719824999996,-0.01613361275,0.054106417000000004,-0.0499455445,0.07898722825,-0.014621054499999998,0.08328554125,-0.02660218675,0.0029891759249999995,-0.010516797999999999,0.03557642825,-0.060217531,0.0171425431,0.03266155825,-0.0598199765,0.021716342,0.0851276085,0.06803951925,0.02062912125,-0.008283969975,-0.032307963655,0.0506742335,0.020532822,0.0360789155,-0.0006283978250000003,-0.02785933975,-0.0030728417500000002,-0.034540906499999996,-0.0300205485,0.1115321695,0.053453546000000005,0.0703359815,0.010377180074999999,0.036756731075,-0.00297751195,-0.077184735,0.0264304635,0.0481822745,0.073460445,0.06669125100000001,0.09291405250000001,0.05010346175,0.029880568250000003,-0.034649416,0.029474834999999998,0.031317542500000004,-0.02699742575,0.0557845065,-0.05035647375,-0.0022147215,0.0213037777,0.07004532675,0.0264894885,-0.04297427725,0.07081584375,-0.00481877925,0.0048342215,-0.02130823725,-0.025961382749999998,-0.06262777725,0.034384046749999994,-0.0190645906,-0.031134641674999998,-0.068910896,0.06261982975,-0.0260708335,0.09122910349999999,-0.03100116965,0.049027657,-0.01474484825,-0.03469707425,-0.006989758326499999,-0.056124094,0.009483469874999999,-0.04452728725,-0.011856436750000001,-0.02315196925,-0.0312197325,0.04668023435,-0.013576097,0.12342725624999999,0.009619299000000001,-0.03253524225,0.00691780145,-0.007575374,0.017970803,0.039913282575,0.01719394935,-0.034085923000000004,0.0160938888,0.047048848750000004,0.05772991225,0.03188324875,0.005430960675,-0.015838408499999998,-0.020243144275,0.017690586,-0.038486903249999996,-0.0037113453749999994,-0.05992632025,-0.0066788766,-0.009260173449999999,-0.043526957500000005,0.035566587250000004,0.0520154155,-0.03742566475,-0.012924420124999999,0.05485878925,0.017260086875,-0.00945986855,0.06038467375,0.028211044,0.01180591125,0.076735146,0.010415625750000001,-0.026288575499999998,0.03359009575,-0.029869059499999996,0.018123453499999997,-0.039763396500000006,0.013718122,0.015185622250000001,-0.021508391,0.0933689765,0.0861815165,0.01772232125,-0.03339793175,-0.018280382249999998,0.024922235325,-0.00670621405,-0.004941027,-0.0030330302750000006,0.049037655,0.0407625665,-0.098270484,-0.010615769,0.014149342125,0.0732888865,0.01031384975,0.0708132685,-0.0248589595,0.01914073765,0.023424223175,0.0017784540000000003,-0.0016458964999999989,0.034801676250000003,0.0007287893750000003,-0.00010908749999999942,-0.10601041950000001,0.035614491750000005,0.0023196674499999996,-0.07413317850000001,-0.03187354875,0.0022230885000000004,-0.022146297500000002,-0.0020944157250000002,-0.053558506000000006,0.005407627825000001,-0.00868874405,0.049080156750000006,-0.015095300350000002,0.050741894499999995,-0.0644765125,0.049679071875,0.011181570100000001,0.08374534125,0.047086234500000004,0.04629623975,0.07211018675,0.01747124065,0.03838733075,-0.02483115725,0.059315212,-0.007435562849999999,-0.033800771,-0.062262683750000006,0.018508591749999997,0.05456382,-0.032214809750000004,-0.0060910296,-0.0688426575,0.028439918925000002,-0.050798488,-0.032318176500000004,0.0070076956499999996,-0.0850821425,0.0116441195,-0.06209254825,0.0392326955,-0.038714852,0.042342809,-0.07345825249999999,-0.0783221335,-0.00245315065,0.0052674726500000005,0.06709293425,-0.033900455,0.059125045,-0.0089869296,0.03726652275,0.036819674749999996,-0.066015052,0.0090962235,-0.0532330595,-0.0423983279,0.054948274,-0.0021313933875,-0.036738087749999995,-0.061552870499999995,0.01083386435,-0.000004987750000000173,0.03250943875,-0.00265433175,0.023262875250000002,0.04094322,-0.0458693885,0.004981979125000001,-0.01146187345,-0.017557853249999998,-0.02698673775,-0.02743749775,0.015717598925,0.00637069715,0.0225431645,-0.024064882425,0.07339412125,0.031813663,0.04107066175,-0.0605346105,0.005952673775,-0.06258660275,-0.0320774,-0.049892332,0.024880522500000002,0.0639617125,-0.0702298615,0.017941317249999998,-0.005220711350000001,0.014933126924999998,0.0115449712]"), - SubjectUID: "", + SubjUID: "", FaceSrc: SrcAuto, SampleRadius: 0, Samples: 1, @@ -54,7 +54,7 @@ var FaceFixtures = FaceMap{ "joe-biden": Face{ ID: "VF7ANLDET2BKZNT4VQWJMMC6HBEFDOG6", EmbeddingJSON: []byte("[0.10730543085474682,-0.007740289179353713,0.04013410115400314,0.01458170011165962,-0.033333988977870946,0.06636234022813034,-0.00010941258007316575,0.0266348918046072,-0.05017391628723953,0.026034562221256254,-0.03388911566430759,-0.03461048494812202,0.040559725024994844,0.02683793627304573,-0.00972269717541027,-0.07836494561032105,-0.022470260049817198,0.011276674801708603,-0.05526434009558201,0.014401617237932205,-0.031258523568474236,-0.05416104192368187,-0.05567222379755878,0.017950877029356768,-0.016397424193561935,0.062346790423413276,-0.019043469394284057,0.04085343435437774,-0.05627231374819698,0.002355368169155769,0.07268979656775187,-0.0015096598716884627,-0.030188596848975374,-0.030941932784964564,-0.02826790015985233,-0.05420075791573048,-0.015742074680253033,0.019360258910790157,-0.008227027287290193,-0.08797317745792674,-0.07358703463505077,0.09688007249803735,0.015168583267354964,-0.034569315837825396,0.054231690688333986,0.018033145214487362,0.01579093209709463,-0.09204238994311237,0.08645247031890774,-0.10499936100221444,0.022421303168151857,0.005288450124515152,-0.017391072021601867,0.011218363053184624,-0.08478270589438915,0.0038618527485391615,-0.023381522484070015,-0.05428399272960853,-0.049397680150033,0.04006855272634697,-0.056704127236808774,-0.00958812557516262,-0.024006645464504622,-0.0073450501057456975,-0.03138197361666756,-0.013765138786817361,0.01162637563227787,0.0023935177775817563,0.08953138773108768,0.05337418268588829,-0.012870218945196915,0.03652425150877475,-0.027783526080406188,-0.019489638927241745,-0.01591402705199299,-0.005031992847164803,-0.014592982936285286,-0.03540697236418762,0.015595597412254449,0.004689344744109726,-0.009276015175478172,0.0058068592886232374,0.10480716412028504,0.0169216338187767,-0.0159497901004467,0.04574707649004688,-0.012214007484710122,-0.04849749380776977,0.054958586523843764,0.055898306713647844,-0.05052226642217827,0.008803732924324036,0.02326267119630642,0.047305830959801676,0.04497242295638694,0.020850376996620942,0.01314765746152954,-0.06768179592533874,0.05844347174572754,-0.03379152370001783,0.009412363744416903,0.04876727547273407,0.03299943491180715,0.01981742466488743,0.0547951049219265,0.020208802772381018,-0.08163521584288311,-0.038910958658009524,-0.004049565234655952,-0.02227413252290535,-0.0176418922441086,0.0568860088455925,-0.03240221023084612,0.0018760896289435579,-0.03234445138420723,0.007601825631139565,0.004916589611196899,-0.07292478312312889,0.021712048014592936,0.008807552270011749,0.0045489283733609,0.018861112444398878,-0.0341377092368577,-0.06305481604926585,0.039113288345403674,-0.01390809621003151,-0.04930861238819008,0.02377057523982868,0.019087416355893325,-0.013899296822125817,0.02251690464443226,0.08074113913260841,-0.018922226267959787,0.07189693789385795,0.060660045672045707,-0.023638294307546808,-0.006141792394255906,-0.06662582397409247,-0.013895529799502565,0.016608829923953898,-0.00390724028582611,0.05038048671591301,-0.015355035841564064,-0.0008532485082750321,-0.004694504582768126,-0.016610601585741958,0.008180847821889228,-0.04035771976174698,0.01847608156922703,0.08409907464663602,-0.029978496458568385,-0.06499117178372192,0.07448235046571827,0.10142187900247382,-0.023405319141915855,0.05237413796294822,0.04315940939233541,-0.02349721355909328,0.012594679585403442,-0.10457832859776592,0.001468614040066719,0.0165479676672657,-0.07708675453700256,-0.05102918249748802,0.045642631412478735,-0.004785828004440499,0.0203317336356945,-0.02006395174473057,0.04201285855375195,0.032883700123707296,0.0477916040669878,0.08070634492548084,-0.09245629058029556,0.05112703265588493,-0.006224603994954872,-0.0005257819460310555,0.005513055457300567,0.02521921247766018,0.012207323409280013,-0.009936333046208725,-0.007426916158089448,0.027260071572856714,0.006004036209329835,-0.039462719505699156,0.04428369084658737,0.005021041270120048,0.00955255667591259,-0.024385389176467896,0.06930311634011002,-0.0389855151682066,0.009325780048200605,0.0067487294106089776,-0.0538568250434906,-0.04132319716445885,0.005287871307727813,0.02836177144018917,0.016369665767238237,-0.02612976718916588,0.0781344821977253,0.0124423230052565,0.007052016124275589,0.07093038059380721,-0.040975969278632354,0.05987170546998787,0.0429845054696949,0.06377765311330413,-0.054260408781722336,0.017124075467253648,0.011034745989844896,-0.01129856537228031,-0.03058279355517101,-0.052326615682374664,0.06340472755555274,-0.007235566082305412,0.08209440086026383,-0.0037407900405261995,-0.02100836190159107,0.051361881913555715,0.035520336595121764,0.019260735587613487,0.04814414379586697,-0.010566343916274241,-0.03353529212573547,0.05283452853813282,-0.027749841873006824,-0.03820509264906912,-0.0015166780129867554,0.02487160170807457,0.03048850776525669,-0.030538799520168875,0.0921192664219265,0.03269134465648327,-0.031787506815418434,-0.01908650508301182,0.05982613160244779,-0.053232109332236294,-0.03650761934345913,0.0026813365359365463,0.032588356136758805,-0.032364926929593085,0.07780626359498405,0.044541174425177764,0.011626562325897788,0.03554684517681643,-0.030510870967539787,-0.04088990689640999,0.07105028789278889,0.03138784011465073,-0.06342823476303319,0.09164142434876824,-0.0112280279000453,-0.04595559070266152,0.08798781996626949,0.026803936697537615,-0.0014241986940294257,-0.020834715982498548,0.023556784775891685,0.008996215819517326,-0.0012677171940084454,-0.07692881668502807,0.024615258007191814,0.02948731386628723,0.06911119150276565,-0.041541930091072085,0.07317672894504547,-0.012252912262506771,-0.03429316172188286,0.03286905748134327,0.025736928383919527,0.003926683415351601,0.006255630871762562,-0.020806247741813468,0.0675457214042778,0.007579460672946357,0.012004441173839569,-0.028187582314963343,-0.0018772867526912688,-0.01844064376148571,-0.05389302147970715,-0.04154738243111,-0.05912346626385308,-0.003186127453911171,-0.015869915592464562,0.036601020266580665,-0.08332522355102062,-0.015594113206121387,0.010554298175920372,0.009863903175943527,-0.04408378851017952,-0.01321298950931368,-0.026788807387387467,-0.00905998101737915,-0.07901183432849217,0.022626760559060342,0.05966787504726859,-0.0373913765745697,-0.00620443077226124,-0.005321248754354935,-0.05629461318153381,-0.04339327553344822,0.03066110013017902,-0.0560899433873785,0.029585001932263853,-0.06142458606396866,0.018855098215825178,0.03336997769082436,-0.07772387048591708,0.02869667860757885,0.04751144987925148,0.07131169258731747,0.01554444873138424,-0.019102520424152183,-0.06713599618322277,0.021553602847260475,0.022784952935549926,-0.07224605423420524,-0.03428428022313595,0.025510370273970604,-0.042455744666400605,0.024999596293880464,0.0007267671517935561,-0.007103063657435513,0.051193967364198685,-0.03918299151588478,-0.05340270113635778,-0.0005553757678619388,-0.04361415384515381,-0.05659870360464592,-0.003001301568729019,-0.10493783691904449,0.007865782491956196,-0.010459198798326888,0.03839990013440418,-0.029396389004837837,0.04123072916591454,-0.003870788638888664,0.011576299454542732,0.021793958225202522,0.0013144587776207917,-0.024084851461598205,-0.007895128372669067,0.02794634672595444,0.013256276108802492,-0.06581846043538475,-0.03512838380870453,0.010219935781849479,0.041956290830379675,-0.02193645334812136,0.036522118692461206,-0.04014683200312634,-0.007509486720670319,0.025035869046040268,0.03341998480559387,-0.03562761249035026,0.04892323307058029,-0.030771232001644132,-0.016917612628533363,0.002604945885121918,-0.044643074882380486,0.01154372547133419,-0.021955625942386627,0.018907365975515553,0.03550167291446045,0.01069377167082758,0.00010183658435096768,-0.04899959038740444,0.04724968668608978,-0.01864932432341235,0.0591259089168789,0.07907125494612216,0.028897156624642945,0.01633692932619137,0.06420496597867965,0.018129071607111358,-0.06522170992608013,-0.03939954941189146,0.04130569647272587,0.04419998725251961,0.04542913027885341,0.018470383181769943,0.008568164058957863,-0.06659949697784996,-0.053012251715078354,-0.020253768667636778,-0.0428765378002491,0.07184141544699574,0.02058260375849676,-0.03779574153167915,0.0021254573788347247,0.00922705617390442,-0.06903300705643031,0.048223514541707424,-0.008124176700399017,0.06623217639861775,0.011399885879904556,0.13320644195552445,-0.015707634324862062,0.004298537653726769,0.007440328888434029,-0.03552852988830433,0.006549433453245544,-0.019685784628289793,0.001693401851796341,0.050209905835451124,0.023254144681681632,-0.04905436637160683,-0.01058279299507389,0.06261640349854469,-0.07554102380998802,-0.010803172210683784,0.04001997347501145,-0.013296409033855438,0.056829244201244355,0.029110596151947783,0.006392164909307861,-0.0035876165295053485,-0.019022594469099045,-0.06487911801050472,0.02178870949222603,0.05293369045270252,0.0014374271403566358,0.02058438161717472,-0.05258523574003887,-0.03312468141761551,0.051533518133239746,0.03929023312081566,-0.07294044148252202,0.01607557897360134,-0.0007034383966050719,0.014925192443655966,0.051449392859268764,-0.06079890988933106,-0.04363216685223599,0.028568039422766974,0.045766175851156804,0.07275596444172669,-0.02276483221781349,0.09294405910429002,0.06625853254336929,-0.04167032707059745,-0.04751508792911625,-0.014774199240300752,0.023224616626467326,-0.01281115503053608,0.03472993899021339,0.008343472536062031,-0.011408440443860645,0.004419146704378701,0.05045044130775452,-0.03518939370823498,-0.04170123182437134,0.022208642446600917,0.07141607704347333,-0.04112406919064011,0.03227901691925602,0.03527487398910847,-0.029543274091153718,0.005872693907862854,-0.008123872357421475,-0.058780187362098696,-0.00027467445847377796,0.024044984289353373,0.057634827237228584,-0.04450547877367153,-0.03946884506688686,-0.02006971111822632,0.006139106799438476,0.014848452844277668,-0.040448605585189826,-0.047422823475079534,0.00047739853115692137,-0.03920787799786568,-0.05102518756346798,0.029106281725284004,0.023013759328845976,-0.0181101632871727,0.003943383191735267,-0.11744085779379082,0.00652325401639185,-0.0016088291387550352,0.004582751362570763,0.06564233218507957,0.014525142593546867,-0.05397913284980278,-0.005146496768864823,0.008265835225847246,-0.09204165418391608,-0.023673615795413973,0.016221329961976054,0.0560354235721693,-0.03387280199538708,0.011243025140723228,0.02789629877560217,0.07942785398379296,0.019745293456116107,-0.03951280953572121,-0.0325216371505229,-0.04877831216997623,0.008021598871560669,0.06607214515587043,0.08340918698473548,-0.06638043362871171,0.0003533690162649157,-0.05787711264029312,0.017585791805968413,-0.004768172475530777,-0.031721018591366806,0.059853391075907716,0.08903246940908241,0.00910143805785122,-0.02198764055408287,0.023417301139897727]"), - SubjectUID: SubjectFixtures.Get("joe-biden").SubjectUID, + SubjUID: SubjectFixtures.Get("joe-biden").SubjUID, FaceSrc: SrcManual, SampleRadius: 2, Samples: 33, @@ -66,7 +66,7 @@ var FaceFixtures = FaceMap{ "jane-doe": Face{ ID: "VF7ANLDET2BKZNT4VQWJMMC6HBEFDOG7", EmbeddingJSON: []byte("[0.0007977288249999999,0.00627412635,0.010081367249999999,0.060607290999999994,0.0198231296325,0.00677942925,0.0127981935,0.0173897118,-0.0031311625250000003,-0.01667410885,-0.063420228,0.018361592,-0.08858173750000001,0.03073611525,-0.031917364749999996,-0.038458696,-0.0547301015,-0.008465875175,0.01761571915,-0.004627128875,-0.014689141,0.0638510155,-0.06016673375,0.0427068645,-0.0018743572750000003,-0.01726785,0.01797843425,0.013435698749999999,-0.02318214925,0.022266988,0.010709568150000001,-0.006173700925,0.05487494425,-0.00735372625,-0.00730452625,-0.009618603875,0.02644855175,0.03581387875,0.0603323515,0.0274974055,-0.011483472,0.018239579750000002,0.0330916205,0.02055146915,0.0242073965,-0.0006206512774999994,-0.01502779865,-0.00663770983,0.020805977000000003,0.025900334499999997,0.03035390375,0.0032148387500000003,-0.00391496355,0.054927406750000005,0.038901408,0.045302014249999994,0.003995124,0.03543321425,0.06059028175,-0.1112412095,-0.0058859643825,0.006096619500000001,0.034855345,-0.0979946125,-0.01321143025,0.11343869000000001,0.006484222825,0.025919567249999997,0.06456440150000001,-0.004858886425,-0.006586231,-0.012453104075,-0.0045703442499999995,0.06079882,-0.0248508515,0.0585631235,0.0204748375,-0.050474988250000005,-0.0601280055,0.0206371795,0.05054124575,0.07786492925,0.06104633175,0.0122962905,-0.020578794,0.04871231525,-0.0046306429,-0.11645592,-0.0016124173750000003,-0.0174920002425,0.0055334826,0.04527567075,0.07851289325,-0.0053165103000000005,-0.02369275525,0.017747101675,-0.0519253465,-0.00209848275,-0.012302984500000001,-0.013717400199999999,0.01359236975,0.02108301525,0.041778528499999995,-0.10003224,0.005552072275,0.024320880750000003,0.011672114249999999,-0.056534426750000005,-0.06916763575,-0.06349918,-0.0056744145,0.0346825085,-0.05831162125,-0.001728813,0.026253652175,-0.00694282505,0.07631711775,0.0162711135,-0.025647805849999997,-0.05708761650000001,-0.1363480425,0.0731495385,0.074582931,0.01708094025,-0.0401903185,-0.014694330250000002,-0.0015122242499999994,-0.0527830695,0.03213205125,0.01814980125,-0.0022534211875,0.006620919325,0.0096743934,-0.00950359475,-0.03451209525,-0.077583193,-0.0327730455,-0.00922135715,0.047522121,0.010230271575,0.0098390256,0.05241573875,-0.0001339010500000001,0.0493660095,0.04127633175,0.0588596835,-0.008334644124999999,-0.013199373475000001,0.00565431415,0.02194862975,0.053116437,-0.06246536,-0.06762575875,0.0083671415,-0.062960726,0.028651681,-0.0047957945,0.02279490325,-0.01949435125,-0.0145249815,0.0179922025,-0.0050103798,-0.02183141475,0.014762827225,-0.0098820205,0.050833761500000005,-0.06879305050000001,-0.019839172457500002,0.03499764575,0.0508008915,-0.0215009885,0.006626853750000001,-0.038975954,0.029960057999999998,0.0196604121,0.010876822750000001,0.02900263525,-0.00548767725,0.013630449250000001,0.0005998975,0.019220962075,-0.0465303345,0.019054888,-0.02356633375,-0.038011357999999995,-0.025119104500000003,0.03871977525,0.024482080750000003,0.08431583125,0.02732495225,0.0949345775,0.0306948015,-0.01885955675,0.0670141655,-0.0106550329825,0.0004984321480000002,0.03305477934,0.03265502,-0.03410266475,0.06414483,-0.042341717,0.0491270175,0.03191559375,0.02826792625,0.1142247775,0.03742189875,0.03385123675,0.043446917,0.0065846527,0.019319251675,0.050761064999999994,-0.03899118075,0.02432739675,0.056898795,-0.07070233249999999,-0.0063754505,-0.0538108965,-0.0519101365,-0.08475072950000001,0.0080495835,-0.08008777975,-0.0499128635,0.0237340165,0.0359055565,-0.043358586500000004,0.031290711,0.0449176795,-0.003326489350000001,0.0373564235,0.017098932,0.058522701499999996,-0.013411767875000001,-0.03544247725,-0.00710222475,-0.026729970750000002,-0.0142197245,-0.002157010249999999,0.05803525749999999,-0.051960914,0.02590911975,0.0300332645,0.0244066115,0.026088825249999996,0.07373419,-0.060161999499999994,-0.0263518045,0.0149505905,0.005806035210000001,-0.07896729899999999,0.006424700950000001,-0.0104141191,0.01328528435,-0.05459953475,0.04573714225,0.047946037999999996,-0.030557348499999998,-0.033911058,0.028284643249999998,0.031623838,-0.017917532,0.038332007,0.04307251675,0.062034384750000005,-0.013643519,-0.06629597425,0.0846901,0.032617285999999995,0.016660794,-0.0103782503,-0.0035999764999999997,-0.04074201625,-0.045392795,-0.0567024655,0.0534449995,0.032140247999999996,-0.002615942875,0.003970428749999999,0.0006793384999999996,0.0479470605,-0.016679134425,0.002493526,-0.007884887625,-0.07609053,-0.06749486625000001,-0.015545290075,-0.02866428275,-0.04221165,0.0099219202,0.03324765825,0.06594442824999999,-0.05207292325,0.0256602945,-0.008784434175000001,-0.020650667,-0.01128855048,-0.026282654000000003,-0.06931673275,-0.03460548625,-0.0039021685000000003,-0.003175222075,-0.025202154450000003,0.03085420725,0.062878601,0.036194557625,0.0315831115,-0.013516679,-0.016713230000000003,0.03202856975,-0.013251297775,0.020228611,0.0753752425,0.08055008,0.08036205,0.05738650225,-0.029444540749999998,0.0182663646,-0.04029768,0.05418008275,-0.00337875545,0.0869958875,0.04981642525,0.021226451,0.067376955,0.05702456425000001,0.0256154065,0.0165551525,0.0573363425,-0.1131940465,-0.048254465999999996,-0.006381563175,-0.02805285125,-0.0843180615,-0.02208099575,0.043981212500000005,-0.049404160749999995,-0.02832630175,-0.019787925749999997,-0.044114692,0.09415466650000001,-0.0715971185,0.053612709999999994,0.0617767915,0.011761299175,-0.0743617975,-0.07687093,-0.04184027125,-0.03814104625,0.0190135215,-0.02036469565,0.0043338230499999995,-0.046716389250000004,0.0342062435,0.0023538994499999995,-0.04717579125,-0.0273496055,0.04244376625,0.03831893575,0.01170549475,-0.03156427225,0.011907790275,-0.060581235,0.041743171499999995,0.099892805,0.08941036024999999,0.07931023225,-0.0045060146,-0.02298178175,0.0170326654,0.002005552125,0.03624607975,-0.00254639081,-0.021702498499999997,0.00025758344999999975,-0.05735207875,-0.058512898,0.054487390999999996,-0.069780037,-0.0482861765,0.0426412765,-0.0005190220499999999,0.026654973999999998,-0.018059142435,0.015806152,0.0052889667500000005,-0.0156201485,0.0704166475,0.060801867999999995,-0.0465132285,-0.020886614749999997,-0.011178703875000001,-0.0090127261,-0.009662520155,0.03789897025,-0.051430011,0.01944026075,0.07463842749999999,0.051795128075,0.011506837975000002,-0.008200053,0.011794975,0.03101000675,-0.049201035000000004,-0.0443442445,-0.0134417219,-0.006325725025,-0.01755221025,-0.0607596845,-0.05817676075,0.015325420199999999,0.026280469425,-0.07285754675,-0.06974749,0.01869944075,-0.023075805624999998,0.0091478072825,0.031091439499999998,-0.006865514,-0.04208474925,-0.0054626954749999995,0.0188026845,-0.049544713000000004,0.032261805,0.02338521525,-0.013330432,-0.0224570155,-0.07082244425,-0.014643495500000001,0.1226712625,0.0132564726,-0.031361429749999996,0.0379521724,-0.0066203463,-0.06123780825,-0.019100098,-0.027406878750000002,-0.0161381114,0.02359161625,0.01835811065,0.0794485855,-0.0399414135,-0.051978358749999995,-0.079630794,-0.054609961750000005,0.0144952945,-0.0032930052749999997,0.1006953125,0.0168597975,0.003768769525,-0.024148695999999997,-0.038411440875,0.0352168475,0.0783426045,-0.06891328699999999,-0.044475929750000004,-0.0476271525,-0.0257017798,0.007319193000000001,-0.045860846,0.03613755025,0.032934875749999995,-0.015392466625,-0.0171107225,-0.021246307,-0.0049659322075,-0.1082791675,-0.04918869425,-0.061089002,0.03516362325,0.033606162499999995,-0.01977699145,-0.029709126250000002,0.09406408499999999,0.023276244,-0.061732811,-0.008581465,-0.0577642725,-0.02751739825,0.0150603045,-0.013947836,0.0069377069999999996,-0.030457946,0.0455026755,0.015736250325,0.04198308775,0.050190193499999994,-0.009795321,-0.0626410775,-0.0029042444000000013,-0.010100665375,0.033087972,0.019320060942499998,-0.03953386525,0.022216985874999998,-0.013800474,-0.010697693999999999,-0.007538092125,0.07874210100000001,-0.013867060100000002,-0.015251711,0.03930575225,0.047296916,0.04652198125,-0.0124463654,0.0721478725,-0.010877033000000001,0.0175319255,0.038609693,0.0295120445,0.011322374,0.07543583300000001,0.00606530779,-0.06794421925]"), - SubjectUID: SubjectFixtures.Get("jane-doe").SubjectUID, + SubjUID: SubjectFixtures.Get("jane-doe").SubjUID, FaceSrc: SrcManual, SampleRadius: 0.2849559839760571, Samples: 3, @@ -78,7 +78,7 @@ var FaceFixtures = FaceMap{ "fa-gr": Face{ ID: "TOSCDXCS4VI3PGIUTCNIQCNI6HSFXQVZ", EmbeddingJSON: []byte("[-0.053561389904586795,0.02493500179508972,0.05595611112191772,0.0055583031357406614,0.06096615248301697,0.03496644994218445,-0.01198695953822937,-0.02454460388852234,0.06803707268757629,0.026374346495635984,0.0829672158716156,0.016856238718595883,0.026665416756958008,-0.0040052483594970704,0.020293359176193237,0.04141028915954589,-0.0203971049396698,-0.022560134065307616,-0.03766844269610596,0.010925087254508973,-0.05584892202276611,-0.01708412063880005,0.07078410664926148,-0.006791854692691954,-0.09048726862814331,0.06529471053547668,0.00805870625440979,0.003185225187793579,-0.018936199966329958,-0.012567245673349,-0.05535705010704651,0.03018335210581665,-0.04415012300550842,-0.05080241808776856,-0.037411959780944824,0.017662395667355345,0.09760594306341552,-0.028251039018463132,0.01704797846333618,-0.059372201955718996,0.05574424936593628,-0.010868599007102965,-0.012860168669677733,0.01699000405715027,0.06789376899803162,-0.007262103997096252,0.02162036994911194,0.029675374175361636,-0.018911653790352638,-0.040019186535003665,-0.05813169548834228,0.001130981756311036,-0.03868822248225403,-0.013124584308215332,-0.04959773636276245,0.015140624265402221,-0.02141350306637573,-0.03751785701907349,-0.006248120308032227,0.05437072424835205,0.018381264600920565,-0.06975018308328552,-0.03373974997651672,0.034049636619096685,-0.04413912348025513,-0.020500543401235964,-0.06727858024015808,0.0056846422075592035,-0.027692957355023196,0.03017583047795105,0.0379142424294281,-0.020554617585459595,0.08608181673811341,-0.00558661152100952,-0.01761539180555359,-0.030357409232269282,-0.038801233812469485,-0.002326377160518799,-0.04056732406237793,-0.03209971834346008,0.024125684115341186,0.0315708265684845,-0.05462144547168274,0.0026592329726211547,-0.0002449828401040651,-0.018294493854022216,-0.004316533692294311,0.031179499626611328,0.018720620290939332,-0.010533231864138794,-0.02601918470727783,0.03628710090163574,0.00506572206867981,0.03785012353689575,-0.02089666075800049,0.0005888406594442138,-0.02709355396816101,0.05639648983508301,0.006216579241943359,-0.05114516002183533,-0.04535121382156372,0.02563305507798462,-0.07923478377371215,0.032685130028892215,-0.001191900266772461,-0.04475778329414673,-0.023464674753723144,-0.002032372990753174,-0.01025872433950653,-0.024260434875390625,0.02864984106276855,0.007080414502122192,-0.06647833907400513,0.0783933927390442,0.08410754442626953,0.048460447177383424,-0.005683048104943848,-0.0033352546661720274,0.02472490765201111,-0.06543308330223084,0.08478776423457336,-0.025810452447610474,0.043794386990188595,-0.05349430982803345,0.06986419394355775,-0.06776561983079529,-0.027279223366226193,0.06643936192025757,-0.02699730024284821,-0.02261310328250824,-0.030541795043481444,-0.036404816941958615,0.002319148392166138,0.030034134980651855,-0.05057250613243866,0.015797195287312316,-0.05546740251412964,-0.03792064480703736,-0.046270072130859374,0.03600284879156433,-0.016286383008148195,-0.01447935282893982,-0.023145600843895997,0.05445904519541932,-0.019425428872716675,-0.027849521253845218,0.02624303381966553,0.03904380140099487,0.014552910185842895,-0.015262209582714847,-0.01546606357798767,0.06973534984918213,0.03443690571838379,-0.03316039928060913,0.025289981915246583,0.027551592510702515,-0.05067044379051208,-0.026268390881219482,0.01957086168347473,-0.018028586132089235,0.03361352139247131,0.02203808638873291,0.01012487246018982,-0.02816909800907898,-0.009651444010451508,-0.09467841339996338,0.02476457462034607,0.004779872280953979,0.0501946385553894,0.02433562254768372,0.018832664526740112,-0.012715093342271423,-0.029919957296679688,0.00032212689794006383,0.01700923572964264,0.009046746358553008,0.005211006430688477,0.0016982382203079224,0.049998209365203855,0.03436742353085327,-0.0121563069042511,0.05672995838252258,-0.04706990067637634,-0.056664633050765995,-0.012870437315783691,-0.07744849265951537,-0.046735237881042474,0.027536489646829223,-0.0063465054498382575,-0.03997399767149353,-0.005338936482995606,-0.00362328916847229,0.001921811812083435,-0.054075812154693606,0.05371017350401306,-0.06654334314242553,0.027277056678497315,-0.07361431532672119,0.029437021327194217,-0.015122589976702879,0.031627708358566285,-0.0755312407050171,0.010630397944732665,0.008918350276101683,-0.05778323354902649,0.0994148142859497,0.04068332303825378,-0.05268494103314209,0.03015451629309845,-0.018775764622164917,-0.009062567619699096,0.007059089237823487,-0.012346531363076781,-0.04320939792871094,0.07813872230740357,-0.03035949564965286,-0.01947474479802704,0.037923704186218266,0.007442268481819458,-0.03807596509654236,-0.008003724362815856,0.025805637528684693,0.03347750500194153,-0.07962477516238403,0.04868631318370056,0.02977127815507507,0.0029135298348171995,0.09915490313339234,0.019012361539624024,0.051748930902374264,-0.06191068098287964,-0.03972761279501037,0.0016844349614929203,0.0792777453282013,-0.031942168768554685,0.046137294803588864,-0.008718205879244996,-0.029874303470267486,-0.027774339395344544,0.05929509779299927,-0.01801204332841797,0.059447149948730474,0.04841970852426147,-0.0011173667728857427,0.022864025326361084,-0.026576215090582278,-0.05152430963276977,-0.022743134079202268,0.02738566810560608,0.025095160937530517,0.030021709013610842,-0.004549830057951355,-0.00030068623583221453,0.0009505420682525637,0.0293544979478833,-0.0007231390724517822,-0.044882692693908696,0.028293255085162353,-0.026880331244885255,-0.0020027976073425297,-0.0035708168297326663,-0.029375177293811037,-0.03760578239718628,0.009499767254864502,-0.006659269350109253,-0.008412711995428467,0.04506797155446625,-0.061588304754226685,-0.009378277797467041,-0.019579266450035097,-0.009406960064926148,-0.006551914738876342,0.05495061562628174,0.02347226618362427,0.01817871002554474,-0.016346725162420958,-0.0217945497718689,0.06450126613516236,0.05860402971525574,-0.0017858836164611814,-0.050239541191390985,0.10140318902853393,0.04010114428864746,0.0008114142740280172,-0.020079750827853394,0.0290547948102417,0.0523606569267334,0.0697007091730957,0.025686350122457888,-0.069663450563797,0.01652778975481415,0.04418237776111389,-0.030442624633346557,0.07070662380650329,0.00607760764486084,0.018585937644244384,0.04747481670280457,0.003442409377093506,-0.027003151967041016,-0.014442290127001954,-0.03355948236605835,0.04752330382379151,-0.06009620755910644,0.012064740791355286,-0.02728147155336609,-0.0021602167619903566,-0.019625289300570677,-0.027403285474594117,-0.03132451562153625,-0.00696172938717041,0.07344502092098999,-0.05291414485440063,-0.027143530911676025,-0.02862010956461487,0.013457262882380066,0.020273073586030577,0.0869318828140564,0.03178946219732208,0.01546345787972107,-0.025812222701647947,-0.012377268964108276,0.025233301823695375,-0.027445998504124852,0.04623188894171143,0.03194692068911133,-0.018809481018737792,0.05440299125515747,0.08841021014102173,0.024042683822097777,-0.0437970808447113,0.0622284837868042,-0.07230914156738281,0.061785600439392095,-0.005898011142715301,0.017018915580725096,-0.0074343364131958,0.0472978119741211,0.03322449719477844,0.05553645137957764,-0.045228417684552005,0.014598348405079651,-0.05135967175909424,0.02576592887980652,0.05089475070780945,0.03544028016654968,-0.03770926128921509,-0.017584528153511046,-0.0048514873775695804,-0.031726442226049804,0.03177034682945251,0.0442558303147583,-0.028826217311273192,-0.07331044327641296,0.0389429476973114,0.002298495944973755,0.05235454000157165,-0.00712197604630127,-0.023682568859283448,0.023732466243682863,0.013723664675314027,0.058904355138183595,-0.005807367322749329,0.015096379596409608,-0.006360169882392883,-0.030699472455233766,-0.0525733884350586,-0.022169618942180988,0.003913205996453857,0.03606074732737366,-0.007866270807852172,0.048841612311386104,0.0034765838286437986,0.004248465200854493,-0.03280500829776306,0.06378764452790833,0.055532332409862675,0.08680252519212342,-0.017844255846798704,0.029810377057144163,-0.02942251447070618,-0.044855546762594845,-0.005517916898706055,0.050234964126266476,0.011523523362924195,0.005936588419967651,-0.03421115212210693,-0.057217579814910886,0.031242944445419314,0.005507734122839355,-0.07523726362719727,0.07727178597763062,-0.03340703129373169,-0.0037740379818069457,-0.05799064666407776,0.002951611914428711,-0.0381551103936615,0.06767114967684937,-0.05226751990200806,-0.08678377352308655,0.018258538073345338,-0.05100995869369507,-0.014669730179177368,0.0019073754601989742,0.019928211831918334,-0.009651354162780762,0.00148156542230835,0.0300498909315033,0.013933203938430786,0.049118417478469845,0.07034309039993286,0.08631518890869141,0.045312744073425296,-0.0114556711665802,0.002262044725527954,0.03909658930291748,0.058989745317047114,-0.09017043097357177,0.029915415012585447,-0.021694081220796205,0.007112899486217041,0.034908875648205565,-0.07557318224520873,0.00747602307928894,-0.07689437854341126,-0.0816104629965149,0.06584847972103881,-0.027223482248062134,-0.0734825446631775,-0.026031234442622377,-0.021221113447901917,0.009427636536148072,0.0733798275839386,0.038499398346939086,0.07065423379763794,-0.03234823392584229,-0.03810496551548004,-0.022686502779409486,-0.0280469574908548,-0.025900615115014645,0.042155878818878174,-0.00046531529118805023,0.0967171226513672,-0.04239019891482544,-0.021407168994793703,-0.0901884771748352,-0.009318602439419555,-0.062206653710449214,-0.05432696328559875,0.07080146096733093,0.010451482704911804,-0.030338169516888426,-0.05439818012039185,-0.012598419564173888,0.04722508374314881,0.005100992507037353,0.07548265095941162,-0.02993898883608093,-0.00834879275138092,-0.04563876436360169,-0.04020596480168152,0.0288604560251709,-0.07016607971246339,-0.021332305549841307,-0.012735979240548707,-0.0325507344491864,-0.027569437057450866,-0.00618333070614624,0.014708548748822022,-0.028020249052243044,-0.0055215683416900635,-0.027922072329937132,-0.04069747037893982,-0.015941954358544924,-0.057657907068542485,0.057477929705368046,0.09361960902160645,0.023154440080284118,0.08209954707691955,-0.04653738135888672,-0.022657983432824706,0.017859119538793943,0.03184386651611328,-0.06479600977639771,-0.037647264635800176,-0.03802839938505555,0.007582054424845885,-0.000219012254373169,-0.019724465482708738,0.008770073973083495,-0.05662031348901367,-0.021091617894259642,0.024251208349423216,-0.08418829827484131,-0.058303197052200315,0.007839132345101807,-0.01063874680109253,-0.10956825240359497,-0.03290552308336639,-0.0166004150932312,-0.007761278100527954,-0.0341763706727066,0.030335803438739012,-0.04574581508354187,-0.040141617028427126,0.029325096495925906,-0.027303054453048703,-0.018120842727615355,-0.081876543736084,0.049446647137298586,-0.015033118148144533,0.0259648896006012,0.03862191597553406]"), - SubjectUID: "", + SubjUID: "", FaceSrc: "", SampleRadius: 0.3335191224530258, Samples: 4, @@ -90,7 +90,7 @@ var FaceFixtures = FaceMap{ "actress-1": Face{ ID: "GMH5NISEEULNJL6RATITOA3TMZXMTMCI", EmbeddingJSON: []byte("[-0.042797452781249995,0.00422465874375,-0.048662441625,0.0031430446,0.06633659487500002,-0.018196528703125,0.040835139765625,0.052952038265625,0.006016549665625001,0.009785659178125,-0.029831666965625,0.054324560015624995,0.01054248685625,0.017850270410937497,0.026022013796875,0.03613610925,0.03469964425,0.07347525625,-0.026205852546875,0.0166165387265625,-0.00219102274375,-0.0521860075625,0.028795398437499998,-0.020569271453125,0.00665820161140625,-0.05419626184375,0.006151694960937499,0.0280780779140625,-0.002380057990625,-0.0047011529390625,-0.0250162446671875,-0.08458884684375001,-0.0488252095625,-0.0263757030625,-0.0141043764,-0.055177046234375,-0.024440984495312502,-0.06736828789062499,-0.011746090509375,-0.0658738613125,-0.012061459415625001,0.006663972303124998,-0.02980980240625,-0.024637781609375003,0.081152222890625,0.08694402428124999,-0.009879704509375,-0.06108930775,0.030863420734375,-0.024178249960937502,-0.09244857825,-0.033086686328125,0.039693733424999995,0.0222100371025,-0.07447425484375,-0.068857844375,-0.023395244290625,-0.004518527246875,-0.0123647139375,-0.019760296765625,-0.04322777059375,-0.0339581230625,0.00922167535,0.13172738974999998,-0.057130676015625,-0.04021859003125,-0.04409194796875,-0.06360571378125,0.0038800593812499994,0.011037305934375,-0.12183968115625,-0.07553384335937499,0.08556041196875,-0.00675970565625,-0.008239945834375,-0.0002750189718749999,-0.0123454039234375,-0.074857799546875,0.039849796187499995,0.004566287940625,0.011853646906250001,0.012255246218375,-0.01633359240890625,-0.02856772603125,-0.025007442953125,0.060898780296874996,-0.058974834484375,0.023121999062500002,-0.020350771803125,-0.030586074459375,-0.033634610171875,0.0538676319375,0.041377327125,-0.028109858159375,0.0072398953984375,0.073216935046875,-0.0314768581875,0.062401044,0.040599449268750004,-0.057796485843749995,-0.05566476365625,0.03588720815625,-0.035500273796875,0.0735189534375,-0.02440577285,-0.094595259875,-0.028696179756250002,-0.00399260396875,-0.009224862356249999,-0.005818649078125,-0.045656088593750005,-0.0070814703171875,-0.026597113656249997,-0.030390899437499997,0.039040696718750004,-0.06426890425,-0.06785859596875,0.010784127853124999,-0.054993149796875,-0.037887456421875,0.03338866509375,-0.0212479521875,0.04566912451874999,-0.043900132531250004,0.0746015890625,-0.0243242484375,0.033496269125,-0.00895349779375,-0.090135792046875,-0.060008334546875,-0.03285268494375,0.0074423431250000005,0.0056082175,0.007676819027812499,0.043098117078125,0.01961326721875,0.043081848421875,0.0017007411484375001,-0.058708293015624996,-0.011024991296875,-0.017065255225,0.0463664746875,-0.050305781265625,0.07986595525000001,0.031914426896875,-0.0451691994375,-0.014531763891874999,0.0238665033034375,0.0019062963437499999,0.037309521359375,-0.056698300125000003,0.05307699465625,0.025364797046875,0.041471147171875,0.013446926728125001,-0.032811845578125,-0.07355180671875,-0.07045656553125,-0.027063845834375003,0.02144017521875,0.03856208171875,0.054781846015625,0.065618965625,0.045270843109375,-0.07020550296875,0.0254819304059375,-0.050949131109375004,0.034050439953125,0.02819093215625,0.003919965578125,-0.022841327275312497,0.0024206921150000003,0.0412864711875,0.011987981381249999,-0.05907508284375,0.028428842559375,-0.04474331540625,0.011173079484375001,0.023104085078125003,0.02738280965625,-0.02268888928125,-0.014092400128749998,-0.005039106766562499,0.02355611371875,0.0366038645,-0.09409493103125,-0.0111312976078125,-0.055360834203125,0.0781101144375,-0.034545760890625,-0.026267615734375,0.0074333465375,-0.016226572784375,-0.0334946088328125,0.0087815724984375,-0.03358945,0.00830140880625,-0.116942932375,0.014852332671875,-0.048670014625,0.07110080234375,-0.010049274725,-0.009037579723437501,-0.026618966031796875,-0.0088876183875,0.07093048070312499,0.0003418017562499999,-0.016285292934375,0.023988535603125,0.02958441390625,0.028762250546875,0.10859091765625,-0.026533771203125,-0.08597097553125,0.0254817276328125,-0.0008839394531250004,-0.03657968334375,0.024961030874999998,-0.009623568225625,-0.018902566154374998,-0.02395361703125,0.08707610598437499,0.006472697634374999,0.012126961531250001,0.086273127234375,0.02112650480625,0.072864588171875,0.038641529375,-0.0339470703796875,-0.0070681386978125,-0.05114889515625,-0.064077736875,0.0801859346875,0.03619106715625,-0.044011980829687505,0.020673788834374998,0.05450474890625,0.0007419524218750008,0.005525969687500001,0.05928241809375,0.00873763790625,0.0075881849671875,-0.0030794112093750006,-0.000550304234375,-0.057828903015625,-0.015375027634375,-0.01341457351875,-0.047298705734375,0.0006185382125000001,0.05260293315625,-0.00247172916125,0.0024320877968749995,0.042094363078125,0.038377400699999994,0.047210891703125,0.026533354281249998,-0.0173795238765625,0.0750090398125,-0.02227682790625,-0.015970143140625004,0.0041755543746875,0.0389908288125,-0.026298664421875,-0.0033195475937500005,0.0507240515625,0.0121328996028125,-0.0159850067625,-0.009988589078124999,-0.004624901678125,-0.053816907359375,-0.027323620906250002,-0.07184708109375,0.0739319004375,-0.005675397015625,0.0574534906875,-0.0018322834937499997,0.012349312390625,0.04371528075,0.102938820046875,0.049753955453125,-0.0471465348125,0.014692125212499999,0.011317236803125,0.01167229329,-0.000906872929687501,0.043713847328125,0.050471491734375,0.09127307840625,0.0037926065499999983,0.02476943571875,0.009150867346875001,-0.00608378619375,0.030803383125,0.0250073956109375,0.0141615992625,-0.009127686290625001,0.015869761743625,-0.010958556390624999,-0.012998547918281249,-0.039504874515624996,0.0106979253125,0.01700042085,-0.002604378640625,0.02084322703125,0.030859358796875,0.07505586726562499,0.019067374809375,-0.10191251765625,-0.026006241906249998,-0.047772568234375,-0.0225939113675,0.0361513486875,-0.00958636515625,0.005696997026562499,0.0009894751187500004,0.03013342728125,-0.00880371684375,0.03674879507812499,0.024124882781250002,-0.029399576015625,-0.024997744675000003,-0.05070753615625,0.0313155348725,0.002575418878125,0.005519026109375,-0.05591307903124999,0.0281892310625,-0.03541215253125,-0.07037230915624999,-0.037092981328125,0.013661958040625,0.010150755915625,0.00464935505625,-0.04271175659375,-0.048959364375,0.034574696328125,0.07233478540624999,0.01784359430625,0.0194107177171875,-0.072066672625,0.014341187478125,-0.006838044025000002,0.02312758503109375,0.053927468859375,-0.02893340253125,0.028794135621875,0.045664569265624996,0.039563965140625004,-0.046501012734375,0.065077949234375,0.04449139196875,0.007535738353125,0.043328710500000006,-0.0111408846265625,0.0476011903125,0.060015349999999995,-0.031573955609374996,0.042777720843750006,0.039751312859375,-0.045557269625,0.015271656046875002,-0.0354880490625,0.08538222071875,0.04966688990625,0.056357682296875,-0.011723008359374999,-0.029344679578124998,-0.0026356525624999998,0.05605940871875,0.0239344324375,0.03265417645,0.0010181780718749999,0.076328233859375,-0.0031451877390625002,0.013421614153125,0.10775338210937499,0.046965805359375,0.029762421328125,-0.057406333812499995,0.05597286525,-0.0060815406249999995,0.03258119896875,0.085884606015625,0.0047218173609375,0.024015754907812503,0.0016205940656249997,0.00977695415,0.0248720199375,0.02193678424375,0.02297564028125,-0.02170162459375,-0.016097422159375,0.0036265405412500003,0.058745011828125,0.014267859934375,0.05047798946875,0.0011764822421875,-0.0066052294703125005,-0.05816269984375,-0.0021058192984375,-0.004860934460937501,-0.11327106140625,-0.0204606366775,-0.0118132771265625,0.011304225534375,0.06806309021875,-0.012411606234374999,0.042336594859375,-0.04588128365625,0.0032292451499999998,0.002116435284375,0.05678600528125,-0.019285949515624998,0.018476397046875002,0.020250683071875,0.029468594703125003,0.047492925046874995,0.025336735062499997,-0.021452310171875003,-0.0055234529,-0.010190777515625,-0.02390919653125,0.054283543546875,-0.0089797622421875,-0.0196026405725,0.0847159765,-0.008061421078125,-0.0096810058890625,0.0025359010559375,-0.070020598203125,-0.071642143765625,0.041324847953125,0.062386370046875,0.02080815875,0.03997223459375,-0.004368696557812499,-0.061350179687500006,-0.0379680811875,0.028993340656249998,-0.018274929603124997,-0.0164239366015625,0.027168593468750002,-0.016546095809062498,0.053955401365624996,-0.0365716815625,0.035633695312500005,0.02898745825,-0.0543745755625,0.02312739328125,0.029107762598437503,-0.014907261559375,-0.0393391562734375,0.0266410936875,-0.003273399434375,-0.0365162964375,0.005375640290625,0.04377122634453125,0.007220736701875,0.0027202672781249998,-0.046226400200000003,-0.019703925796875,0.0267226415203125,-0.0313677562890625,-0.018290202359375,0.0116326953546875,0.09366585175,-0.044070507687499996,-0.01559544946875,0.081022457265625,0.0634066775625,-0.048881354125,0.0031107287671875003,-0.02833384091875,-0.0053156806781249994,0.079171438671875,0.07384103956249999,-0.0623021398125,0.06140550584375,0.0487548321875,0.0005419291718749999,-0.02468360628125,-0.064845638109375,-0.038376832953125,-0.023363560046875,0.010565507843749999,-0.02082457664375,-0.010381079196875,-0.03493705171875,0.0830325743125,-0.024592210421875,-0.055243339578125,-0.039002618171875,-0.068491207046875,-0.003042380805,0.0229049883125,-0.045181625687500004,0.0007183075796874999,0.00022675300312499994,0.010043045665625002,0.06768922384375001,0.006879946099999999,-0.031594079140624995,-0.07930963040625,-0.0192133980625,-0.07839539546874999,0.0880084346875,0.022715694093750002,-0.016752869099999998,0.03304357490625,-0.08040172209374999,-0.03786231021875,-0.019575631078125003]"), - SubjectUID: SubjectFixtures.Get("actress-1").SubjectUID, + SubjUID: SubjectFixtures.Get("actress-1").SubjUID, FaceSrc: "", SampleRadius: 0.27852392873736237, Samples: 4, @@ -102,7 +102,7 @@ var FaceFixtures = FaceMap{ "actor-1": Face{ ID: "PI6A2XGOTUXEFI7CBF4KCI5I2I3JEJHS", EmbeddingJSON: []byte("[0.029390826499999998,-0.0503012115,-0.0038545094999999988,0.0509694235,0.031093432499999997,0.0496081305,0.029921502000000003,-0.011004544,-0.060369486,0.017543245,0.01045707515,-0.099466162,-0.00480026345,-0.04493461,-0.0037644171749999997,-0.065234118,-0.0040764575,0.054459017,0.022406583,0.0096438375,-0.110314265,0.016590626,-0.0228292765,-0.1048702,-0.0763408,-0.006629400999999999,0.004310676500000001,-0.029119649,0.01620591965,0.015485259999999999,0.008847230999999999,0.05485049,-0.034671595,0.0115840045,-0.007419231,-0.093930635,0.0017309952,-0.0048228687,-0.05732045499999999,-0.033113999500000005,-0.0240111475,0.0323214015,0.00287205625,-0.030934734,0.00156168245,-0.048679148000000005,0.02641126,-0.0112433873,0.012481417250000001,-0.065828795,-0.03307603,-0.0117135982,0.048581928999999996,0.028982857,0.005623224499999999,-0.032749998,0.011234884625,-0.0006842143500000002,0.026518498,0.061320491,-0.0146354356,-0.079900327,0.0683921215,0.007673159000000001,-0.0036813074999999997,-0.0450446075,0.02894235215,-0.039071353,-0.045876458,-0.0352887335,-0.07320109999999999,-0.015539629999999999,0.0289246405,-0.049284593,-0.01250326,-0.0337509495,0.046399666,-0.071127149,0.08158958499999999,-0.0426160325,-0.0080168428,0.0136308075,0.02569515,0.043871697,0.0033857435,0.058685176000000006,-0.01749069625,-0.053259678,0.049114284,-0.039802356,0.0080722875,-0.0296135,-0.0043557191000000006,0.0014750100000000006,-0.009338751650000001,0.0276961965,0.00885040575,0.016889396,0.0085062253,0.0206174708,0.026253349723,-0.037348458,0.019311317,0.012004956,-0.018269215,-0.0280598629,-0.042533084000000006,-0.04640435,0.003540217,-0.032545467,-0.013042198,0.069853378,-0.0463841565,-0.027688966500000002,-0.059565311999999995,0.0955551455,-0.00281534995,-0.00056369025,0.056012735,0.064883204,-0.004080717500000001,-0.0589001255,0.0204381845,0.00573572365,0.0227016705,-0.0095095435,0.049776725499999994,-0.0318561775,0.013517221,0.05898059,0.0099591405,-0.0467890275,0.020263018,0.0853258175,0.0098792975,0.0248817635,-0.044659859999999996,-0.022181526,0.0473887315,0.022208215000000003,-0.03485217,0.068261088,-0.01371124975,0.023889135500000002,-0.004693188599999999,0.061553551,-0.000011999000000000384,-0.0158730365,-0.006931088,0.09489299500000001,0.055977795,0.06602641000000001,0.01054402925,-0.01124513895,0.002503631,0.0072078265,-0.014834385499999998,-0.046107534000000006,-0.007856566499999999,-0.0422721125,-0.0423789265,-0.0212909215,-0.00794488725,-0.0469044435,-0.071824202,0.0225725725,-0.0319154,0.026527513,-0.009888712899999999,0.02953384,0.085858678,0.013506947,0.0678732135,0.042902051499999996,-0.062338507,0.0354902925,-0.01188858515,0.0286265445,0.019621773500000002,-0.02367736825,0.031068825,0.071462768,0.0546697175,-0.028328653999999998,0.0338450425,0.0414083,0.064161343,-0.014787015499999999,0.042576257,-0.052504155999999996,-0.009431747,0.054281609999999994,-0.021365432499999996,-0.010823029500000001,0.031537834,0.027937777499999997,-0.051239634,-0.0008470490000000008,0.0361261665,-0.0580307265,-0.0315868765,-0.0055125591500000005,0.01400582125,-0.0184045163,-0.046149815,-0.005499571,0.0155790405,0.0583802535,0.039215091,0.0008411794999999998,-0.041732420000000006,-0.050039497,0.007283665000000001,0.0431485005,-0.00677296875,0.0601975825,0.046510825,0.023623201,-0.01524039935,0.07721521,0.074249245,0.0407936515,0.00849325405,0.02431605585,0.011315986,0.01000438185,0.000004355499999999894,-0.0256082855,0.027331278,0.026526522,0.045884533000000005,0.064017159,-0.035494845,0.014849005,-0.07764901299999999,-0.046943987,-0.004783612,-0.0019289365000000006,0.044880989999999996,-0.0615054725,0.008713913,-0.064159369,-0.108074245,-0.0245115032,-0.0346285845,-0.060331363,-0.0337360495,-0.0928075975,0.023803201500000003,0.0215816405,0.039825595000000005,-0.023639452000000002,0.01902793,-0.00781150095,-0.046295049,0.015229914,-0.044872945,-0.0181105295,0.018318828500000002,-0.085241333,-0.00763042895,0.045985624,0.078587918,0.0237581805,0.019066963,-0.014184376,0.0314069,-0.0208157225,0.013775869999999999,-0.092092003,0.060038437,0.0562684335,0.01282044765,-0.043142775,-0.077564127,0.0211277365,-0.013994697,0.0015711942999999998,0.0226966145,0.099338087,0.0291462905,0.093184027,-0.045245903500000004,0.0520713405,-0.069911768,0.0341777035,-0.0035331141,0.005852487999999998,0.0426323475,-0.0029093645,-0.03406806,-0.00466204385,-0.020408429999999998,0.07412659699999999,0.0555368515,0.035822505500000004,-0.019567507499999998,-0.0317213775,0.0006759230000000001,-0.0320736795,0.01196724155,0.0319717745,0.02698861,-0.0009364279,-0.0356625695,0.011958187,0.0374467985,0.0545301175,-0.09391315,0.048995292999999995,-0.04715971,-0.01018323015,-0.02479688875,-0.034046104650000006,0.0157533374,-0.0087085585,-0.00108834735,-0.0443082275,0.0403492,0.009395348,0.0366207795,0.055315856499999996,0.0932063675,-0.00557909673,0.0502743045,-0.08796758,-0.0131685065,-0.026735282,-0.0241287825,-0.0381497525,0.0056856297,0.033867326,0.05904681,-0.0679266085,-0.0335014595,0.032558654,0.041723721,0.017434435499999998,-0.045156486499999995,-0.029794047499999997,-0.0027904705000000004,0.020673108750000002,-0.0192013875,-0.0299808695,-0.03081326245,0.00125815955,-0.075886538,-0.04229379,0.039182544,-0.003952550000000001,-0.0143515505,-0.0330433685,-0.030146457499999998,-0.026057409,-0.0164466015,-0.053462672499999996,0.01185499725,0.028812123999999998,-0.0368386395,0.0232451215,-0.01108514465,-0.007439916499999999,-0.033431644499999996,0.046813843,0.01008481285,-0.0181223735,-0.0159577681,-0.0033102907500000005,-0.034488053000000005,0.06935485,0.0379800925,0.0325609615,-0.0733929885,0.024054515999999998,-0.09723248000000001,-0.0443772245,-0.021324694499999998,0.0103385705,0.055247911499999997,-0.0556644675,-0.0431905165,-0.039941268,-0.0346950015,-0.0587926,-0.0163988131,0.027000692,-0.00154901115,-0.018861745,0.016058999650000003,0.0137215953,0.033349326,0.01698440525,-0.00412060125,0.0015065590000000002,0.046087469000000006,0.054306643,-0.033629904,0.002419951,0.0393065175,-0.072762359,-0.05446597,0.008974321,-0.068849205,0.105380715,0.08792094249999999,-0.01965546775,-0.0735049575,0.011614979,-0.01172863955,0.019627030900000002,0.023582547000000002,0.029378214,-0.00484438543,-0.04092678,0.0227958605,0.00478901805,-0.0441016925,0.009984607,-0.0034369715,0.08558102000000001,-0.0366258695,-0.07055201,0.0458065275,0.029891107,0.0032485696499999998,0.0416649925,-0.006772525850000001,0.0266587225,-0.037922555499999996,0.0450138765,-0.00031528125,-0.024871631499999998,0.0259149825,0.008122148,0.0082324845,-0.01057201585,-0.02999843425,-0.068303585,-0.00490130815,0.030564248999999998,-0.053122275,-0.029750361000000003,-0.022997867999999998,-0.0175231255,0.031132409,0.0018893698000000002,-0.0022311989,0.0064160765,-0.04024497,-0.105197768,0.00480071625,0.0125502665,0.0240250565,-0.03232351,0.054054075,-0.00419136315,-0.0627546015,-0.048507734999999996,-0.0187147505,0.0251195347,0.0294110515,0.11645791,0.039039331499999996,0.0519267515,-0.0652619505,-0.0144988885,0.134864065,-0.0372120825,-0.036438243,-0.0330626875,-0.031956766,-0.094570108,-0.0443558225,0.09487513,0.0673500685,0.0307305825,0.010350817415,-0.052975773500000004,-0.021578872,0.057038174999999997,-0.0035335995999999995,0.0053534458,0.0302415495,-0.061024507000000006,0.0104371503,0.000001594999999999635,-0.089924738,-0.00963383915,-0.051207721,0.062871485,-0.0676942725,-0.01274987815,-0.033068996,-0.0032600508500000005,-0.0482352155,-0.0400539235,0.04832185,0.0171953418,0.0162546295,0.052375985,0.043627676500000004,-0.060105043999999996,0.0125964665,0.0040247465,-0.0207374005,-0.0084052233,-0.031770002,-0.02711991625,0.0463117675,-0.0348302765,-0.0784637,0.0499060845]"), - SubjectUID: SubjectFixtures.Get("actor-1").SubjectUID, + SubjUID: SubjectFixtures.Get("actor-1").SubjUID, FaceSrc: "", SampleRadius: 0.3239983399779298, Samples: 4, diff --git a/internal/entity/face_fixtures_test.go b/internal/entity/face_fixtures_test.go index bf90b0bb9..8ce9910db 100644 --- a/internal/entity/face_fixtures_test.go +++ b/internal/entity/face_fixtures_test.go @@ -9,7 +9,7 @@ import ( func TestFaceMap_Get(t *testing.T) { t.Run("get existing face", func(t *testing.T) { r := FaceFixtures.Get("jane-doe") - assert.Equal(t, "jqy1y111h1njaaab", r.SubjectUID) + assert.Equal(t, "jqy1y111h1njaaab", r.SubjUID) assert.Equal(t, "VF7ANLDET2BKZNT4VQWJMMC6HBEFDOG7", r.ID) assert.IsType(t, Face{}, r) }) @@ -23,7 +23,7 @@ func TestFaceMap_Get(t *testing.T) { func TestFaceMap_Pointer(t *testing.T) { t.Run("get existing face", func(t *testing.T) { r := FaceFixtures.Pointer("jane-doe") - assert.Equal(t, "jqy1y111h1njaaab", r.SubjectUID) + assert.Equal(t, "jqy1y111h1njaaab", r.SubjUID) assert.Equal(t, "VF7ANLDET2BKZNT4VQWJMMC6HBEFDOG7", r.ID) assert.IsType(t, &Face{}, r) }) diff --git a/internal/entity/face_test.go b/internal/entity/face_test.go index 6f2252d0e..215ae1a3c 100644 --- a/internal/entity/face_test.go +++ b/internal/entity/face_test.go @@ -133,7 +133,7 @@ func TestNewFace(t *testing.T) { r := NewFace("123", SrcAuto, e) assert.Equal(t, "", r.FaceSrc) - assert.Equal(t, "123", r.SubjectUID) + assert.Equal(t, "123", r.SubjUID) }) } @@ -185,7 +185,7 @@ func TestFace_Save(t *testing.T) { assert.Nil(t, FindFace(m.ID)) m.Save() assert.NotNil(t, FindFace(m.ID)) - assert.Equal(t, "12345fde", FindFace(m.ID).SubjectUID) + assert.Equal(t, "12345fde", FindFace(m.ID).SubjUID) } func TestFace_Update(t *testing.T) { @@ -193,11 +193,11 @@ func TestFace_Update(t *testing.T) { assert.Nil(t, FindFace(m.ID)) m.Save() assert.NotNil(t, FindFace(m.ID)) - assert.Equal(t, "12345fdef", FindFace(m.ID).SubjectUID) + assert.Equal(t, "12345fdef", FindFace(m.ID).SubjUID) m2 := FindFace(m.ID) - m2.Update("SubjectUID", "new") - assert.Equal(t, "new", FindFace(m.ID).SubjectUID) + m2.Update("SubjUID", "new") + assert.Equal(t, "new", FindFace(m.ID).SubjUID) } func TestFace_RefreshPhotos(t *testing.T) { @@ -212,12 +212,12 @@ func TestFirstOrCreateFace(t *testing.T) { t.Run("create new face", func(t *testing.T) { m := NewFace("12345unique", SrcAuto, Embeddings{Embedding{99}, Embedding{2}}) r := FirstOrCreateFace(m) - assert.Equal(t, "12345unique", r.SubjectUID) + assert.Equal(t, "12345unique", r.SubjUID) }) t.Run("return existing entity", func(t *testing.T) { m := FaceFixtures.Pointer("joe-biden") r := FirstOrCreateFace(m) - assert.Equal(t, "jqy3y652h8njw0sx", r.SubjectUID) + assert.Equal(t, "jqy3y652h8njw0sx", r.SubjUID) assert.Equal(t, 33, r.Samples) }) } diff --git a/internal/entity/file.go b/internal/entity/file.go index 69a8eeb37..097833a43 100644 --- a/internal/entity/file.go +++ b/internal/entity/file.go @@ -420,8 +420,8 @@ func (m *File) AddFaces(faces face.Faces) { } // AddFace adds a face marker to the file. -func (m *File) AddFace(f face.Face, subjectUID string) { - marker := *NewFaceMarker(f, *m, subjectUID) +func (m *File) AddFace(f face.Face, subjUID string) { + marker := *NewFaceMarker(f, *m, subjUID) if markers := m.Markers(); !markers.Contains(marker) { markers.Append(marker) diff --git a/internal/entity/marker.go b/internal/entity/marker.go index 00d4a8d99..d74d04e52 100644 --- a/internal/entity/marker.go +++ b/internal/entity/marker.go @@ -34,9 +34,10 @@ type Marker struct { MarkerSrc string `gorm:"type:VARBINARY(8);default:'';" json:"Src" yaml:"Src,omitempty"` MarkerName string `gorm:"type:VARCHAR(255);" json:"Name" yaml:"Name,omitempty"` MarkerInvalid bool `json:"Invalid" yaml:"Invalid,omitempty"` - SubjectUID string `gorm:"type:VARBINARY(42);index:idx_markers_subject_uid_src;" json:"SubjectUID" yaml:"SubjectUID,omitempty"` - SubjectSrc string `gorm:"type:VARBINARY(8);index:idx_markers_subject_uid_src;default:'';" json:"SubjectSrc" yaml:"SubjectSrc,omitempty"` - subject *Subject `gorm:"foreignkey:SubjectUID;association_foreignkey:SubjectUID;association_autoupdate:false;association_autocreate:false;association_save_reference:false"` + MarkerReview bool `json:"Review" yaml:"Review,omitempty"` + SubjUID string `gorm:"type:VARBINARY(42);index:idx_markers_subj_uid_src;" json:"SubjUID" yaml:"SubjUID,omitempty"` + SubjSrc string `gorm:"type:VARBINARY(8);index:idx_markers_subj_uid_src;default:'';" json:"SubjSrc" yaml:"SubjSrc,omitempty"` + subject *Subject `gorm:"foreignkey:SubjUID;association_foreignkey:SubjUID;association_autoupdate:false;association_autocreate:false;association_save_reference:false"` FaceID string `gorm:"type:VARBINARY(42);index;" json:"FaceID" yaml:"FaceID,omitempty"` FaceDist float64 `gorm:"default:-1" json:"FaceDist" yaml:"FaceDist,omitempty"` face *Face `gorm:"foreignkey:FaceID;association_foreignkey:ID;association_autoupdate:false;association_autocreate:false;association_save_reference:false"` @@ -49,7 +50,6 @@ type Marker struct { H float32 `gorm:"type:FLOAT;" json:"H" yaml:"H,omitempty"` Size int `gorm:"default:-1" json:"Size" yaml:"Size,omitempty"` Score int `gorm:"type:SMALLINT" json:"Score" yaml:"Score,omitempty"` - Review bool `json:"Review" yaml:"Review,omitempty"` MatchedAt *time.Time `sql:"index" json:"MatchedAt" yaml:"MatchedAt,omitempty"` CreatedAt time.Time UpdatedAt time.Time @@ -57,7 +57,7 @@ type Marker struct { // TableName returns the entity database table name. func (Marker) TableName() string { - return "markers_dev8" + return "markers_dev9" } // BeforeCreate creates a random UID if needed before inserting a new row to the database. @@ -70,14 +70,14 @@ func (m *Marker) BeforeCreate(scope *gorm.Scope) error { } // NewMarker creates a new entity. -func NewMarker(file File, area crop.Area, subjectUID, markerSrc, markerType string) *Marker { +func NewMarker(file File, area crop.Area, subjUID, markerSrc, markerType string) *Marker { m := &Marker{ FileUID: file.FileUID, FileHash: file.FileHash, CropArea: area.String(), MarkerSrc: markerSrc, MarkerType: markerType, - SubjectUID: subjectUID, + SubjUID: subjUID, X: area.X, Y: area.Y, W: area.W, @@ -89,12 +89,12 @@ func NewMarker(file File, area crop.Area, subjectUID, markerSrc, markerType stri } // NewFaceMarker creates a new entity. -func NewFaceMarker(f face.Face, file File, subjectUID string) *Marker { - m := NewMarker(file, f.CropArea(), subjectUID, SrcImage, MarkerFace) +func NewFaceMarker(f face.Face, file File, subjUID string) *Marker { + m := NewMarker(file, f.CropArea(), subjUID, SrcImage, MarkerFace) m.Size = f.Size() m.Score = f.Score - m.Review = f.Score < 30 + m.MarkerReview = f.Score < 30 m.FaceDist = -1 m.EmbeddingsJSON = f.EmbeddingsJSON() m.LandmarksJSON = f.RelativeLandmarksJSON() @@ -121,13 +121,13 @@ func (m *Marker) SaveForm(f form.Marker) error { changed = true } - if m.Review != f.Review { - m.Review = f.Review + if m.MarkerReview != f.MarkerReview { + m.MarkerReview = f.MarkerReview changed = true } - if f.SubjectSrc == SrcManual && strings.TrimSpace(f.MarkerName) != "" { - m.SubjectSrc = SrcManual + if f.SubjSrc == SrcManual && strings.TrimSpace(f.MarkerName) != "" { + m.SubjSrc = SrcManual m.MarkerName = txt.Title(txt.Clip(f.MarkerName, txt.ClipDefault)) if err := m.SyncSubject(true); err != nil { @@ -172,21 +172,21 @@ func (m *Marker) SetFace(f *Face, dist float64) (updated bool, err error) { } // Any reason we don't want to set a new face for this marker? - if m.SubjectSrc == SrcAuto || f.SubjectUID == "" || m.SubjectUID == "" || f.SubjectUID == m.SubjectUID { + if m.SubjSrc == SrcAuto || f.SubjUID == "" || m.SubjUID == "" || f.SubjUID == m.SubjUID { // Don't skip if subject wasn't set manually, or subjects match. } else if reported, err := f.ResolveCollision(m.Embeddings()); err != nil { return false, err } else if reported { - log.Infof("faces: collision of marker %s, subject %s, face %s, subject %s, source %s", m.MarkerUID, m.SubjectUID, f.ID, f.SubjectUID, m.SubjectSrc) + log.Infof("faces: collision of marker %s, subject %s, face %s, subject %s, source %s", m.MarkerUID, m.SubjUID, f.ID, f.SubjUID, m.SubjSrc) return false, nil } else { return false, nil } // Update face with known subject from marker? - if m.SubjectSrc == SrcAuto || m.SubjectUID == "" || f.SubjectUID != "" { + if m.SubjSrc == SrcAuto || m.SubjUID == "" || f.SubjUID != "" { // Don't update if face has a known subject, or marker subject is unknown. - } else if err = f.SetSubjectUID(m.SubjectUID); err != nil { + } else if err = f.SetSubjectUID(m.SubjUID); err != nil { return false, err } @@ -194,7 +194,7 @@ func (m *Marker) SetFace(f *Face, dist float64) (updated bool, err error) { m.face = f // Skip update if the same face is already set. - if m.SubjectUID == f.SubjectUID && m.FaceID == f.ID { + if m.SubjUID == f.SubjUID && m.FaceID == f.ID { // Update matching timestamp. m.MatchedAt = TimePointer() return false, m.Updates(Values{"MatchedAt": m.MatchedAt}) @@ -202,8 +202,8 @@ func (m *Marker) SetFace(f *Face, dist float64) (updated bool, err error) { // Remember current values for comparison. faceID := m.FaceID - subjectUID := m.SubjectUID - SubjectSrc := m.SubjectSrc + subjUID := m.SubjUID + subjSrc := m.SubjSrc m.FaceID = f.ID m.FaceDist = dist @@ -211,7 +211,7 @@ func (m *Marker) SetFace(f *Face, dist float64) (updated bool, err error) { if m.FaceDist < 0 { faceEmbedding := f.Embedding() - // Calculate smallest distance to embeddings. + // Calculate the smallest distance to embeddings. for _, e := range m.Embeddings() { if len(e) != len(faceEmbedding) { continue @@ -223,8 +223,8 @@ func (m *Marker) SetFace(f *Face, dist float64) (updated bool, err error) { } } - if f.SubjectUID != "" { - m.SubjectUID = f.SubjectUID + if f.SubjUID != "" { + m.SubjUID = f.SubjUID } if err = m.SyncSubject(false); err != nil { @@ -232,18 +232,18 @@ func (m *Marker) SetFace(f *Face, dist float64) (updated bool, err error) { } // Update face subject? - if m.SubjectSrc == SrcAuto || m.SubjectUID == "" || f.SubjectUID == m.SubjectUID { + if m.SubjSrc == SrcAuto || m.SubjUID == "" || f.SubjUID == m.SubjUID { // Not needed. - } else if err = f.SetSubjectUID(m.SubjectUID); err != nil { + } else if err = f.SetSubjectUID(m.SubjUID); err != nil { return false, err } - updated = m.FaceID != faceID || m.SubjectUID != subjectUID || m.SubjectSrc != SubjectSrc + updated = m.FaceID != faceID || m.SubjUID != subjUID || m.SubjSrc != subjSrc // Update matching timestamp. m.MatchedAt = TimePointer() - if err := m.Updates(Values{"FaceID": m.FaceID, "FaceDist": m.FaceDist, "SubjectUID": m.SubjectUID, "SubjectSrc": m.SubjectSrc, "Review": false, "MatchedAt": m.MatchedAt}); err != nil { + if err := m.Updates(Values{"FaceID": m.FaceID, "FaceDist": m.FaceDist, "SubjUID": m.SubjUID, "SubjSrc": m.SubjSrc, "MarkerReview": false, "MatchedAt": m.MatchedAt}); err != nil { return false, err } else if !updated { return false, nil @@ -261,20 +261,20 @@ func (m *Marker) SyncSubject(updateRelated bool) (err error) { subj := m.Subject() - if subj == nil || m.SubjectSrc == SrcAuto { + if subj == nil || m.SubjSrc == SrcAuto { return nil } // Update subject with marker name? - if m.MarkerName == "" || subj.SubjectName == m.MarkerName { + if m.MarkerName == "" || subj.SubjName == m.MarkerName { // Do nothing. } else if subj, err = subj.UpdateName(m.MarkerName); err != nil { return err } else if subj != nil { // Update subject fields in case it was merged. m.subject = subj - m.SubjectUID = subj.SubjectUID - m.MarkerName = subj.SubjectName + m.SubjUID = subj.SubjUID + m.MarkerName = subj.SubjName } // Create known face for subject? @@ -285,21 +285,21 @@ func (m *Marker) SyncSubject(updateRelated bool) (err error) { } // Update related markers? - if m.FaceID == "" || m.SubjectUID == "" { + if m.FaceID == "" || m.SubjUID == "" { // Do nothing. - } else if res := Db().Model(&Face{}).Where("id = ? AND subject_uid = ''", m.FaceID).Update("SubjectUID", m.SubjectUID); res.Error != nil { + } else if res := Db().Model(&Face{}).Where("id = ? AND subj_uid = ''", m.FaceID).Update("SubjUID", m.SubjUID); res.Error != nil { return fmt.Errorf("%s (update known face)", err) } else if !updateRelated { return nil } else if err := Db().Model(&Marker{}). Where("marker_uid <> ?", m.MarkerUID). Where("face_id = ?", m.FaceID). - Where("subject_src = ?", SrcAuto). - Where("subject_uid <> ?", m.SubjectUID). - Updates(Values{"SubjectUID": m.SubjectUID, "SubjectSrc": SrcAuto, "Review": false}).Error; err != nil { + Where("subj_src = ?", SrcAuto). + Where("subj_uid <> ?", m.SubjUID). + Updates(Values{"SubjUID": m.SubjUID, "SubjSrc": SrcAuto, "MarkerReview": false}).Error; err != nil { return fmt.Errorf("%s (update related markers)", err) } else if res.RowsAffected > 0 && m.face != nil { - log.Debugf("marker: matched %s with %s", subj.SubjectName, m.FaceID) + log.Debugf("marker: matched %s with %s", subj.SubjName, m.FaceID) return m.face.RefreshPhotos() } @@ -342,7 +342,7 @@ func (m *Marker) SubjectName() string { if m.MarkerName != "" { return m.MarkerName } else if s := m.Subject(); s != nil { - return s.SubjectName + return s.SubjName } return "" @@ -351,27 +351,27 @@ func (m *Marker) SubjectName() string { // Subject returns the matching subject or nil. func (m *Marker) Subject() (subj *Subject) { if m.subject != nil { - if m.SubjectUID == m.subject.SubjectUID { + if m.SubjUID == m.subject.SubjUID { return m.subject } } // Create subject? - if m.SubjectSrc != SrcAuto && m.MarkerName != "" && m.SubjectUID == "" { - if subj = NewSubject(m.MarkerName, SubjectPerson, m.SubjectSrc); subj == nil { + if m.SubjSrc != SrcAuto && m.MarkerName != "" && m.SubjUID == "" { + if subj = NewSubject(m.MarkerName, SubjPerson, m.SubjSrc); subj == nil { return nil } else if subj = FirstOrCreateSubject(subj); subj == nil { log.Debugf("marker: invalid subject %s", txt.Quote(m.MarkerName)) return nil } else { m.subject = subj - m.SubjectUID = subj.SubjectUID + m.SubjUID = subj.SubjUID } return m.subject } - m.subject = FindSubject(m.SubjectUID) + m.subject = FindSubject(m.SubjUID) return m.subject } @@ -384,7 +384,7 @@ func (m *Marker) ClearSubject(src string) error { } // Update index & resolve collisions. - if err := m.Updates(Values{"MarkerName": "", "FaceID": "", "FaceDist": -1.0, "SubjectUID": "", "SubjectSrc": src}); err != nil { + if err := m.Updates(Values{"MarkerName": "", "FaceID": "", "FaceDist": -1.0, "SubjUID": "", "SubjSrc": src}); err != nil { return err } else if m.face == nil { m.subject = nil @@ -411,14 +411,14 @@ func (m *Marker) Face() (f *Face) { } // Add face if size - if m.SubjectSrc != SrcAuto && m.FaceID == "" { + if m.SubjSrc != SrcAuto && m.FaceID == "" { if m.Size < face.ClusterMinSize || m.Score < face.ClusterMinScore { log.Debugf("faces: skipped adding face for low-quality marker %s, size %d, score %d", m.MarkerUID, m.Size, m.Score) return nil } else if emb := m.Embeddings(); len(emb) == 0 { log.Warnf("marker: %s has no embeddings", m.MarkerUID) return nil - } else if f = NewFace(m.SubjectUID, m.SubjectSrc, emb); f == nil { + } else if f = NewFace(m.SubjUID, m.SubjSrc, emb); f == nil { log.Warnf("marker: failed adding face for id %s", m.MarkerUID) return nil } else if f = FirstOrCreateFace(f); f == nil { @@ -452,9 +452,9 @@ func (m *Marker) ClearFace() (updated bool, err error) { m.MatchedAt = TimePointer() // Remove subject if set automatically. - if m.SubjectSrc == SrcAuto { - m.SubjectUID = "" - err = m.Updates(Values{"FaceID": "", "FaceDist": -1.0, "SubjectUID": "", "MatchedAt": m.MatchedAt}) + if m.SubjSrc == SrcAuto { + m.SubjUID = "" + err = m.Updates(Values{"FaceID": "", "FaceDist": -1.0, "SubjUID": "", "MatchedAt": m.MatchedAt}) } else { err = m.Updates(Values{"FaceID": "", "FaceDist": -1.0, "MatchedAt": m.MatchedAt}) } diff --git a/internal/entity/marker_fixtures.go b/internal/entity/marker_fixtures.go index c4f3e89e3..f3f44b1da 100644 --- a/internal/entity/marker_fixtures.go +++ b/internal/entity/marker_fixtures.go @@ -27,7 +27,7 @@ var MarkerFixtures = MarkerMap{ "1000003-1": Marker{ //Photo04 MarkerUID: "mqu0xs11qekk9jx8", FileUID: "ft2es39w45bnlqdw", - SubjectUID: "jqu0xs11qekk9jx8", + SubjUID: "jqu0xs11qekk9jx8", MarkerSrc: SrcImage, MarkerType: MarkerLabel, X: 0.308333, @@ -40,7 +40,7 @@ var MarkerFixtures = MarkerMap{ "1000003-2": Marker{ //Photo04 MarkerUID: "mt9k3pw1wowuy3c3", FileUID: "ft2es39w45bnlqdw", - SubjectUID: "lt9k3pw1wowuy3c3", + SubjUID: "lt9k3pw1wowuy3c3", FaceID: "LRG2HJBDZE66LYG7Q5SRFXO2MDTOES52", MarkerName: "Unknown", MarkerSrc: SrcImage, @@ -55,7 +55,7 @@ var MarkerFixtures = MarkerMap{ "1000003-3": Marker{ //Photo04 MarkerUID: "mt9k3pw1wowuy111", FileUID: "ft2es39w45bnlqdw", - SubjectUID: "", + SubjUID: "", MarkerSrc: SrcImage, MarkerType: MarkerLabel, MarkerName: "Center", @@ -69,7 +69,7 @@ var MarkerFixtures = MarkerMap{ "1000003-4": Marker{ //Photo04 MarkerUID: "mt9k3pw1wowuy222", FileUID: "ft2es39w45bnlqdw", - SubjectUID: "", + SubjUID: "", MarkerSrc: SrcImage, MarkerType: MarkerFace, MarkerName: "Jens Mander", @@ -86,8 +86,8 @@ var MarkerFixtures = MarkerMap{ MarkerUID: "mt9k3pw1wowuy333", FileUID: "ft2es39w45bnlqdw", FaceID: FaceFixtures.Get("unknown").ID, - SubjectUID: "", - SubjectSrc: SrcAuto, + SubjUID: "", + SubjSrc: SrcAuto, MarkerSrc: SrcImage, MarkerType: MarkerFace, MarkerName: "Corn McCornface", @@ -105,8 +105,8 @@ var MarkerFixtures = MarkerMap{ FileUID: "ft2es39w45bnlqdw", FaceID: FaceFixtures.Get("john-doe").ID, FaceDist: 0.2, - SubjectSrc: SrcAuto, - SubjectUID: "", + SubjSrc: SrcAuto, + SubjUID: "", MarkerSrc: SrcImage, MarkerType: MarkerFace, MarkerName: "", @@ -124,8 +124,8 @@ var MarkerFixtures = MarkerMap{ FileUID: "ft2es49qhhinlple", FaceID: FaceFixtures.Get("fa-gr").ID, FaceDist: 0.5, - SubjectSrc: "", - SubjectUID: "", + SubjSrc: "", + SubjUID: "", MarkerSrc: SrcImage, MarkerType: MarkerFace, MarkerName: "", @@ -143,8 +143,8 @@ var MarkerFixtures = MarkerMap{ FileUID: "ft2es49qhhinlple", FaceID: FaceFixtures.Get("fa-gr").ID, FaceDist: 0.6, - SubjectSrc: SrcAuto, - SubjectUID: "", + SubjSrc: SrcAuto, + SubjUID: "", MarkerSrc: SrcImage, MarkerType: MarkerFace, MarkerName: "", @@ -162,8 +162,8 @@ var MarkerFixtures = MarkerMap{ FileUID: "ft2es49w15bnlqdw", FaceID: FaceFixtures.Get("fa-gr").ID, FaceDist: 0.6, - SubjectSrc: SrcAuto, - SubjectUID: "", + SubjSrc: SrcAuto, + SubjUID: "", MarkerSrc: SrcImage, MarkerType: MarkerFace, MarkerName: "", @@ -181,8 +181,8 @@ var MarkerFixtures = MarkerMap{ FileUID: "ft8es39w45bnlqdw", FaceID: FaceFixtures.Get("fa-gr").ID, FaceDist: 0.6, - SubjectSrc: SrcAuto, - SubjectUID: "", + SubjSrc: SrcAuto, + SubjUID: "", MarkerSrc: SrcImage, MarkerType: MarkerFace, MarkerName: "", @@ -201,8 +201,8 @@ var MarkerFixtures = MarkerMap{ FaceID: FaceFixtures.Get("actress-1").ID, CropArea: "045038063041", FaceDist: 0.26852392873736236, - SubjectSrc: SrcManual, - SubjectUID: SubjectFixtures.Get("actress-1").SubjectUID, + SubjSrc: SrcManual, + SubjUID: SubjectFixtures.Get("actress-1").SubjUID, MarkerSrc: SrcImage, MarkerType: MarkerFace, MarkerName: "Actress A", @@ -221,8 +221,8 @@ var MarkerFixtures = MarkerMap{ FaceID: FaceFixtures.Get("actress-1").ID, CropArea: "046045043065", FaceDist: 0.4507357278575355, - SubjectSrc: "", - SubjectUID: SubjectFixtures.Get("actress-1").SubjectUID, + SubjSrc: "", + SubjUID: SubjectFixtures.Get("actress-1").SubjUID, MarkerSrc: SrcImage, MarkerType: MarkerFace, MarkerName: "", @@ -241,8 +241,8 @@ var MarkerFixtures = MarkerMap{ FaceID: FaceFixtures.Get("actress-1").ID, CropArea: "05403304060446", FaceDist: 0.5099754448545762, - SubjectSrc: "", - SubjectUID: SubjectFixtures.Get("actress-1").SubjectUID, + SubjSrc: "", + SubjUID: SubjectFixtures.Get("actress-1").SubjUID, MarkerSrc: SrcImage, MarkerType: MarkerFace, MarkerName: "", @@ -260,8 +260,8 @@ var MarkerFixtures = MarkerMap{ FileUID: "ft3es39w45bnlqdw", FaceID: FaceFixtures.Get("actor-1").ID, FaceDist: 0.5223304453393212, - SubjectSrc: "", - SubjectUID: SubjectFixtures.Get("actor-1").SubjectUID, + SubjSrc: "", + SubjUID: SubjectFixtures.Get("actor-1").SubjUID, MarkerSrc: SrcImage, MarkerType: MarkerFace, MarkerName: "", @@ -279,8 +279,8 @@ var MarkerFixtures = MarkerMap{ FileUID: "ft2es39q45bnlqd0", FaceID: FaceFixtures.Get("actor-1").ID, FaceDist: 0.5088545446490167, - SubjectSrc: "", - SubjectUID: SubjectFixtures.Get("actor-1").SubjectUID, + SubjSrc: "", + SubjUID: SubjectFixtures.Get("actor-1").SubjUID, MarkerSrc: SrcImage, MarkerType: MarkerFace, MarkerName: "", @@ -298,8 +298,8 @@ var MarkerFixtures = MarkerMap{ FileUID: "fikjs39w45bnlqdw", FaceID: FaceFixtures.Get("actor-1").ID, FaceDist: 0.3139983399779298, - SubjectSrc: "", - SubjectUID: SubjectFixtures.Get("actor-1").SubjectUID, + SubjSrc: "", + SubjUID: SubjectFixtures.Get("actor-1").SubjUID, MarkerSrc: SrcImage, MarkerType: MarkerFace, MarkerName: "", @@ -317,8 +317,8 @@ var MarkerFixtures = MarkerMap{ FileUID: "ft8es39w45bnlqdw", FaceID: FaceFixtures.Get("actor-1").ID, FaceDist: 0.3139983399779298, - SubjectSrc: "", - SubjectUID: SubjectFixtures.Get("actor-1").SubjectUID, + SubjSrc: "", + SubjUID: SubjectFixtures.Get("actor-1").SubjUID, MarkerSrc: SrcImage, MarkerType: MarkerFace, MarkerName: "", diff --git a/internal/entity/marker_json.go b/internal/entity/marker_json.go index 4eaec96bc..9fa1d356e 100644 --- a/internal/entity/marker_json.go +++ b/internal/entity/marker_json.go @@ -13,48 +13,48 @@ func (m *Marker) MarshalJSON() ([]byte, error) { if subj = m.Subject(); subj == nil { name = m.MarkerName } else { - name = subj.SubjectName + name = subj.SubjName } return json.Marshal(&struct { - UID string - FileUID string - FileHash string - CropArea string - Type string - Src string - Name string - Invalid bool - Review bool - FaceID string - SubjectUID string - SubjectSrc string - X float32 - Y float32 - W float32 `json:",omitempty"` - H float32 `json:",omitempty"` - Size int `json:",omitempty"` - Score int `json:",omitempty"` - CreatedAt time.Time + UID string + FileUID string + FileHash string + CropArea string + Type string + Src string + Name string + Invalid bool + Review bool + FaceID string + SubjUID string + SubjSrc string + X float32 + Y float32 + W float32 `json:",omitempty"` + H float32 `json:",omitempty"` + Size int `json:",omitempty"` + Score int `json:",omitempty"` + CreatedAt time.Time }{ - UID: m.MarkerUID, - FileUID: m.FileUID, - FileHash: m.FileHash, - CropArea: m.CropArea, - Type: m.MarkerType, - Src: m.MarkerSrc, - Name: name, - Invalid: m.MarkerInvalid, - Review: m.Review, - FaceID: m.FaceID, - SubjectUID: m.SubjectUID, - SubjectSrc: m.SubjectSrc, - X: m.X, - Y: m.Y, - W: m.W, - H: m.H, - Size: m.Size, - Score: m.Score, - CreatedAt: m.CreatedAt, + UID: m.MarkerUID, + FileUID: m.FileUID, + FileHash: m.FileHash, + CropArea: m.CropArea, + Type: m.MarkerType, + Src: m.MarkerSrc, + Name: name, + Invalid: m.MarkerInvalid, + Review: m.MarkerReview, + FaceID: m.FaceID, + SubjUID: m.SubjUID, + SubjSrc: m.SubjSrc, + X: m.X, + Y: m.Y, + W: m.W, + H: m.H, + Size: m.Size, + Score: m.Score, + CreatedAt: m.CreatedAt, }) } diff --git a/internal/entity/marker_test.go b/internal/entity/marker_test.go index cec4fdd0e..0e048dc9d 100644 --- a/internal/entity/marker_test.go +++ b/internal/entity/marker_test.go @@ -27,7 +27,7 @@ func TestNewMarker(t *testing.T) { assert.Equal(t, "ft8es39w45bnlqdw", m.FileUID) assert.Equal(t, "2cad9168fa6acc5c5c2965ddf6ec465ca42fd818", m.FileHash) assert.Equal(t, "1340ce163163", m.CropArea) - assert.Equal(t, "lt9k3pw1wowuy3c3", m.SubjectUID) + assert.Equal(t, "lt9k3pw1wowuy3c3", m.SubjUID) assert.Equal(t, SrcImage, m.MarkerSrc) assert.Equal(t, MarkerLabel, m.MarkerType) } @@ -38,16 +38,16 @@ func TestMarker_SaveForm(t *testing.T) { m2 := MarkerFixtures.Get("fa-gr-2") m3 := MarkerFixtures.Get("fa-gr-3") - assert.Empty(t, m.SubjectUID) - assert.Empty(t, m2.SubjectUID) - assert.Empty(t, m3.SubjectUID) + assert.Empty(t, m.SubjUID) + assert.Empty(t, m2.SubjUID) + assert.Empty(t, m3.SubjUID) m.MarkerInvalid = true m.Score = 50 //set new name - f := form.Marker{SubjectSrc: SrcManual, MarkerName: "Jane Doe", MarkerInvalid: false} + f := form.Marker{SubjSrc: SrcManual, MarkerName: "Jane Doe", MarkerInvalid: false} err := m.SaveForm(f) @@ -55,20 +55,20 @@ func TestMarker_SaveForm(t *testing.T) { t.Fatal(err) } - assert.NotEmpty(t, m.SubjectUID) + assert.NotEmpty(t, m.SubjUID) if s := m.Subject(); s != nil { - assert.Equal(t, "Jane Doe", s.SubjectName) + assert.Equal(t, "Jane Doe", s.SubjName) } if m := FindMarker("mt9k3pw1wowuy777"); m != nil { - assert.Equal(t, "Jane Doe", m.Subject().SubjectName) + assert.Equal(t, "Jane Doe", m.Subject().SubjName) } if m := FindMarker("mt9k3pw1wowuy888"); m != nil { - assert.Equal(t, "Jane Doe", m.Subject().SubjectName) + assert.Equal(t, "Jane Doe", m.Subject().SubjName) } // Rename subject. - f3 := form.Marker{SubjectSrc: SrcManual, MarkerName: "Franzilein", MarkerInvalid: false} + f3 := form.Marker{SubjSrc: SrcManual, MarkerName: "Franzilein", MarkerInvalid: false} if m := FindMarker("mt9k3pw1wowuy777"); m == nil { t.Fatal("result is nil") @@ -77,13 +77,13 @@ func TestMarker_SaveForm(t *testing.T) { } if m := FindMarker("mt9k3pw1wowuy666"); m != nil { - assert.Equal(t, "Franzilein", m.Subject().SubjectName) + assert.Equal(t, "Franzilein", m.Subject().SubjName) } if m := FindMarker("mt9k3pw1wowuy777"); m != nil { - assert.Equal(t, "Franzilein", m.Subject().SubjectName) + assert.Equal(t, "Franzilein", m.Subject().SubjName) } if m := FindMarker("mt9k3pw1wowuy888"); m != nil { - assert.Equal(t, "Franzilein", m.Subject().SubjectName) + assert.Equal(t, "Franzilein", m.Subject().SubjName) } }) } @@ -93,7 +93,7 @@ func TestUpdateOrCreateMarker(t *testing.T) { m := NewMarker(FileFixtures.Get("exampleFileName.jpg"), testArea, "lt9k3pw1wowuy3c3", SrcImage, MarkerLabel) assert.IsType(t, &Marker{}, m) assert.Equal(t, "ft8es39w45bnlqdw", m.FileUID) - assert.Equal(t, "lt9k3pw1wowuy3c3", m.SubjectUID) + assert.Equal(t, "lt9k3pw1wowuy3c3", m.SubjUID) assert.Equal(t, SrcImage, m.MarkerSrc) assert.Equal(t, MarkerLabel, m.MarkerType) @@ -227,10 +227,10 @@ func TestMarker_ClearSubject(t *testing.T) { m3 := MarkerFixtures.Get("actor-a-2") // id 16 m4 := MarkerFixtures.Get("actor-a-1") // id 15 - assert.Equal(t, "jqy1y111h1njaaad", m.SubjectUID) - assert.Equal(t, "jqy1y111h1njaaad", m2.SubjectUID) - assert.Equal(t, "jqy1y111h1njaaad", m3.SubjectUID) - assert.Equal(t, "jqy1y111h1njaaad", m4.SubjectUID) + assert.Equal(t, "jqy1y111h1njaaad", m.SubjUID) + assert.Equal(t, "jqy1y111h1njaaad", m2.SubjUID) + assert.Equal(t, "jqy1y111h1njaaad", m3.SubjUID) + assert.Equal(t, "jqy1y111h1njaaad", m4.SubjUID) assert.NotNil(t, m.Face()) assert.NotNil(t, m2.Face()) assert.NotNil(t, m3.Face()) @@ -260,10 +260,10 @@ func TestMarker_ClearSubject(t *testing.T) { assert.NotNil(t, FindMarker("mt9k3pw1wowu1002")) assert.NotNil(t, FindFace("PI6A2XGOTUXEFI7CBF4KCI5I2I3JEJHS")) - assert.Empty(t, m.SubjectUID) - assert.Equal(t, "", FindMarker("mt9k3pw1wowu1004").SubjectUID) - assert.Equal(t, "", FindMarker("mt9k3pw1wowu1003").SubjectUID) - assert.Equal(t, "", FindMarker("mt9k3pw1wowu1002").SubjectUID) + assert.Empty(t, m.SubjUID) + assert.Equal(t, "", FindMarker("mt9k3pw1wowu1004").SubjUID) + assert.Equal(t, "", FindMarker("mt9k3pw1wowu1003").SubjUID) + assert.Equal(t, "", FindMarker("mt9k3pw1wowu1002").SubjUID) assert.Empty(t, m.FaceID) assert.Equal(t, "", FindMarker("mt9k3pw1wowu1004").FaceID) assert.Equal(t, "", FindMarker("mt9k3pw1wowu1003").FaceID) @@ -385,43 +385,43 @@ func TestMarker_HasFace(t *testing.T) { } func TestMarker_Subject(t *testing.T) { - t.Run("EmptySubjectUID", func(t *testing.T) { - m := Marker{SubjectUID: "", subject: &Subject{SubjectUID: "", SubjectName: "Test Subject"}} + t.Run("EmptySubjUID", func(t *testing.T) { + m := Marker{SubjUID: "", subject: &Subject{SubjUID: "", SubjName: "Test Subject"}} if s := m.Subject(); s == nil { t.Fatal("return value must not be nil") } else { - assert.Equal(t, "Test Subject", s.SubjectName) - assert.Equal(t, "", m.SubjectUID) - assert.Equal(t, "", s.SubjectUID) + assert.Equal(t, "Test Subject", s.SubjName) + assert.Equal(t, "", m.SubjUID) + assert.Equal(t, "", s.SubjUID) } }) - t.Run("ConflictingSubjectUID", func(t *testing.T) { - m := Marker{SubjectUID: "", subject: &Subject{SubjectUID: "xyz", SubjectName: "Test Subject"}} + t.Run("ConflictingSubjUID", func(t *testing.T) { + m := Marker{SubjUID: "", subject: &Subject{SubjUID: "xyz", SubjName: "Test Subject"}} if s := m.Subject(); s != nil { t.Fatal("return value must be nil") } }) - t.Run("SubjectSrcAuto", func(t *testing.T) { - m := Marker{SubjectSrc: SrcAuto, SubjectUID: "", MarkerName: "Hans Mayer"} + t.Run("SubjSrcAuto", func(t *testing.T) { + m := Marker{SubjSrc: SrcAuto, SubjUID: "", MarkerName: "Hans Mayer"} if s := m.Subject(); s != nil { t.Fatal("return value must be nil") } else { assert.Equal(t, "Hans Mayer", m.MarkerName) - assert.Empty(t, m.SubjectUID) - assert.Equal(t, SrcAuto, m.SubjectSrc) + assert.Empty(t, m.SubjUID) + assert.Equal(t, SrcAuto, m.SubjSrc) } }) - t.Run("SubjectSrcManual", func(t *testing.T) { - m := Marker{SubjectSrc: SrcManual, SubjectUID: "", MarkerName: "Hans Mayer"} + t.Run("SubjSrcManual", func(t *testing.T) { + m := Marker{SubjSrc: SrcManual, SubjUID: "", MarkerName: "Hans Mayer"} if s := m.Subject(); s == nil { t.Fatal("return value must not be nil") } else { - assert.Equal(t, "Hans Mayer", s.SubjectName) - assert.NotEmpty(t, s.SubjectUID) + assert.Equal(t, "Hans Mayer", s.SubjName) + assert.NotEmpty(t, s.SubjUID) } }) } @@ -453,11 +453,11 @@ func TestMarker_GetFace(t *testing.T) { if f := m.Face(); f == nil { t.Fatal("return value must not be nil") } else { - assert.Equal(t, "jqy3y652h8njw0sx", f.SubjectUID) + assert.Equal(t, "jqy3y652h8njw0sx", f.SubjUID) } }) t.Run("low quality marker", func(t *testing.T) { - m := Marker{FaceID: "", SubjectSrc: SrcManual, Size: 130} + m := Marker{FaceID: "", SubjSrc: SrcManual, Size: 130} assert.Nil(t, m.Face()) }) @@ -465,7 +465,7 @@ func TestMarker_GetFace(t *testing.T) { m := Marker{ FaceID: "", EmbeddingsJSON: MarkerFixtures.Get("actress-a-1").EmbeddingsJSON, - SubjectSrc: SrcManual, + SubjSrc: SrcManual, Size: 160, Score: 40, } @@ -499,13 +499,13 @@ func TestMarker_SetFace(t *testing.T) { assert.Equal(t, "", m.FaceID) }) t.Run("skip same face", func(t *testing.T) { - m := Marker{MarkerType: MarkerFace, SubjectUID: "jqu0xs11qekk9jx8", FaceID: "99876uyt"} - updated, _ := m.SetFace(&Face{ID: "99876uyt", SubjectUID: "jqu0xs11qekk9jx8"}, -1) + m := Marker{MarkerType: MarkerFace, SubjUID: "jqu0xs11qekk9jx8", FaceID: "99876uyt"} + updated, _ := m.SetFace(&Face{ID: "99876uyt", SubjUID: "jqu0xs11qekk9jx8"}, -1) assert.False(t, updated) assert.Equal(t, "99876uyt", m.FaceID) }) t.Run("set new face", func(t *testing.T) { - m := Marker{MarkerUID: "mqyz9x61edicxf8j", MarkerType: MarkerFace, SubjectUID: "", FaceID: ""} + m := Marker{MarkerUID: "mqyz9x61edicxf8j", MarkerType: MarkerFace, SubjUID: "", FaceID: ""} updated, _ := m.SetFace(FaceFixtures.Pointer("john-doe"), -1) assert.True(t, updated) diff --git a/internal/entity/photo_counts.go b/internal/entity/photo_counts.go index a1da75c50..703b01dac 100644 --- a/internal/entity/photo_counts.go +++ b/internal/entity/photo_counts.go @@ -65,7 +65,7 @@ func UpdatePhotoCounts() (err error) { if err = Db().Table(Subject{}.TableName()). UpdateColumn("file_count", gorm.Expr("(SELECT COUNT(*) FROM files f "+ fmt.Sprintf( - "JOIN %s m ON f.file_uid = m.file_uid AND m.subject_uid = %s.subject_uid ", + "JOIN %s m ON f.file_uid = m.file_uid AND m.subj_uid = %s.subj_uid ", Marker{}.TableName(), Subject{}.TableName())+ " WHERE m.marker_invalid = 0 AND f.deleted_at IS NULL)")).Error; err != nil { diff --git a/internal/entity/subject.go b/internal/entity/subject.go index 18c2bdac0..bd7bb32a4 100644 --- a/internal/entity/subject.go +++ b/internal/entity/subject.go @@ -20,19 +20,19 @@ type Subjects []Subject // Subject represents a named photo subject, typically a person. type Subject struct { - SubjectUID string `gorm:"type:VARBINARY(42);primary_key;auto_increment:false;" json:"UID" yaml:"UID"` + SubjUID string `gorm:"type:VARBINARY(42);primary_key;auto_increment:false;" json:"UID" yaml:"UID"` Thumb string `gorm:"type:VARBINARY(128);index;default:''" json:"Thumb,omitempty" yaml:"Thumb,omitempty"` ThumbSrc string `gorm:"type:VARBINARY(8);default:''" json:"ThumbSrc,omitempty" yaml:"ThumbSrc,omitempty"` - SubjectType string `gorm:"type:VARBINARY(8);default:''" json:"Type,omitempty" yaml:"Type,omitempty"` - SubjectSrc string `gorm:"type:VARBINARY(8);default:''" json:"Src,omitempty" yaml:"Src,omitempty"` - SubjectSlug string `gorm:"type:VARBINARY(255);index;default:''" json:"Slug" yaml:"-"` - SubjectName string `gorm:"type:VARCHAR(255);unique_index;default:''" json:"Name" yaml:"Name"` - SubjectAlias string `gorm:"type:VARCHAR(255);default:''" json:"Alias" yaml:"Alias"` - SubjectBio string `gorm:"type:TEXT;default:''" json:"Bio" yaml:"Bio,omitempty"` - SubjectNotes string `gorm:"type:TEXT;default:''" json:"Notes,omitempty" yaml:"Notes,omitempty"` - Favorite bool `gorm:"default:false" json:"Favorite" yaml:"Favorite,omitempty"` - Private bool `gorm:"default:false" json:"Private" yaml:"Private,omitempty"` - Excluded bool `gorm:"default:false" json:"Excluded" yaml:"Excluded,omitempty"` + SubjType string `gorm:"type:VARBINARY(8);default:''" json:"Type,omitempty" yaml:"Type,omitempty"` + SubjSrc string `gorm:"type:VARBINARY(8);default:''" json:"Src,omitempty" yaml:"Src,omitempty"` + SubjSlug string `gorm:"type:VARBINARY(255);index;default:''" json:"Slug" yaml:"-"` + SubjName string `gorm:"type:VARCHAR(255);unique_index;default:''" json:"Name" yaml:"Name"` + SubjAlias string `gorm:"type:VARCHAR(255);default:''" json:"Alias" yaml:"Alias"` + SubjBio string `gorm:"type:TEXT;default:''" json:"Bio" yaml:"Bio,omitempty"` + SubjNotes string `gorm:"type:TEXT;default:''" json:"Notes,omitempty" yaml:"Notes,omitempty"` + SubjFavorite bool `gorm:"default:false" json:"Favorite" yaml:"Favorite,omitempty"` + SubjPrivate bool `gorm:"default:false" json:"Private" yaml:"Private,omitempty"` + SubjExcluded bool `gorm:"default:false" json:"Excluded" yaml:"Excluded,omitempty"` FileCount int `gorm:"default:0" json:"Files" yaml:"-"` MetadataJSON json.RawMessage `gorm:"type:MEDIUMBLOB;" json:"Metadata,omitempty" yaml:"Metadata,omitempty"` CreatedAt time.Time `json:"CreatedAt" yaml:"-"` @@ -42,38 +42,38 @@ type Subject struct { // TableName returns the entity database table name. func (Subject) TableName() string { - return "subjects_dev8" + return "subjects_dev9" } // BeforeCreate creates a random UID if needed before inserting a new row to the database. func (m *Subject) BeforeCreate(scope *gorm.Scope) error { - if rnd.IsUID(m.SubjectUID, 'j') { + if rnd.IsUID(m.SubjUID, 'j') { return nil } - return scope.SetColumn("SubjectUID", rnd.PPID('j')) + return scope.SetColumn("SubjUID", rnd.PPID('j')) } // NewSubject returns a new entity. -func NewSubject(name, subjectType, subjectSrc string) *Subject { - if subjectType == "" { - subjectType = SubjectPerson +func NewSubject(name, subjType, subjSrc string) *Subject { + if subjType == "" { + subjType = SubjPerson } - subjectName := txt.Title(txt.Clip(name, txt.ClipDefault)) - subjectSlug := slug.Make(txt.Clip(name, txt.ClipSlug)) + subjName := txt.Title(txt.Clip(name, txt.ClipDefault)) + subjSlug := slug.Make(txt.Clip(name, txt.ClipSlug)) // Name is required. - if subjectName == "" || subjectSlug == "" { + if subjName == "" || subjSlug == "" { return nil } result := &Subject{ - SubjectSlug: subjectSlug, - SubjectName: subjectName, - SubjectType: subjectType, - SubjectSrc: subjectSrc, - FileCount: 1, + SubjSlug: subjSlug, + SubjName: subjName, + SubjType: subjType, + SubjSrc: subjSrc, + FileCount: 1, } return result @@ -101,10 +101,12 @@ func (m *Subject) Delete() error { return nil } - log.Infof("subject: deleting %s %s", m.SubjectType, txt.Quote(m.SubjectName)) + log.Infof("subject: deleting %s %s", m.SubjType, txt.Quote(m.SubjName)) + + event.EntitiesDeleted("subjects", []string{m.SubjUID}) if m.IsPerson() { - event.EntitiesDeleted("people", []string{m.SubjectUID}) + event.EntitiesDeleted("people", []string{m.SubjUID}) event.Publish("count.people", event.Data{ "count": -1, }) @@ -123,11 +125,12 @@ func (m *Subject) Restore() error { if m.Deleted() { m.DeletedAt = nil - log.Infof("subject: restoring %s %s", m.SubjectType, txt.Quote(m.SubjectName)) + log.Infof("subject: restoring %s %s", m.SubjType, txt.Quote(m.SubjName)) + + event.EntitiesCreated("subjects", []*Subject{m}) if m.IsPerson() { event.EntitiesCreated("people", []*Person{m.Person()}) - event.Publish("count.people", event.Data{ "count": 1, }) @@ -153,14 +156,16 @@ func (m *Subject) Updates(values interface{}) error { func FirstOrCreateSubject(m *Subject) *Subject { if m == nil { return nil - } else if m.SubjectName == "" { + } else if m.SubjName == "" { return nil } - if found := FindSubjectByName(m.SubjectName); found != nil { + if found := FindSubjectByName(m.SubjName); found != nil { return found } else if createErr := m.Create(); createErr == nil { - log.Infof("subject: added %s %s", m.SubjectType, txt.Quote(m.SubjectName)) + log.Infof("subject: added %s %s", m.SubjType, txt.Quote(m.SubjName)) + + event.EntitiesCreated("subjects", []*Subject{m}) if m.IsPerson() { event.EntitiesCreated("people", []*Person{m.Person()}) @@ -170,10 +175,10 @@ func FirstOrCreateSubject(m *Subject) *Subject { } return m - } else if found = FindSubjectByName(m.SubjectName); found != nil { + } else if found = FindSubjectByName(m.SubjName); found != nil { return found } else { - log.Errorf("subject: %s while creating %s", createErr, txt.Quote(m.SubjectName)) + log.Errorf("subject: %s while creating %s", createErr, txt.Quote(m.SubjName)) } return nil @@ -187,7 +192,7 @@ func FindSubject(s string) *Subject { result := Subject{} - db := Db().Where("subject_uid = ?", s) + db := Db().Where("subj_uid = ?", s) if err := db.First(&result).Error; err != nil { return nil @@ -205,7 +210,7 @@ func FindSubjectByName(s string) *Subject { result := Subject{} // Search database. - db := UnscopedDb().Where("subject_name LIKE ?", s).First(&result) + db := UnscopedDb().Where("subj_name LIKE ?", s).First(&result) if err := db.First(&result).Error; err != nil { return nil @@ -213,9 +218,9 @@ func FindSubjectByName(s string) *Subject { // Restore if currently deleted. if err := result.Restore(); err != nil { - log.Errorf("subject: %s could not be restored", result.SubjectUID) + log.Errorf("subject: %s could not be restored", result.SubjUID) } else { - log.Debugf("subject: %s restored", result.SubjectUID) + log.Debugf("subject: %s restored", result.SubjUID) } return &result @@ -223,7 +228,7 @@ func FindSubjectByName(s string) *Subject { // IsPerson tests if the subject is a person. func (m *Subject) IsPerson() bool { - return m.SubjectType == SubjectPerson + return m.SubjType == SubjPerson } // Person creates and returns a Person based on this subject. @@ -239,8 +244,8 @@ func (m *Subject) SetName(name string) error { return fmt.Errorf("subject: name must not be empty") } - m.SubjectName = txt.Title(newName) - m.SubjectSlug = slug.Make(txt.Clip(name, txt.ClipSlug)) + m.SubjName = txt.Title(newName) + m.SubjSlug = slug.Make(txt.Clip(name, txt.ClipSlug)) return nil } @@ -249,15 +254,17 @@ func (m *Subject) SetName(name string) error { func (m *Subject) UpdateName(name string) (*Subject, error) { if err := m.SetName(name); err != nil { return m, err - } else if err := m.Updates(Values{"SubjectName": m.SubjectName, "SubjectSlug": m.SubjectSlug}); err == nil { - log.Infof("subject: renamed %s %s", m.SubjectType, txt.Quote(m.SubjectName)) + } else if err := m.Updates(Values{"SubjName": m.SubjName, "SubjSlug": m.SubjSlug}); err == nil { + log.Infof("subject: renamed %s %s", m.SubjType, txt.Quote(m.SubjName)) + + event.EntitiesUpdated("subjects", []*Subject{m}) if m.IsPerson() { event.EntitiesUpdated("people", []*Person{m.Person()}) } return m, m.UpdateMarkerNames() - } else if existing := FindSubjectByName(m.SubjectName); existing == nil { + } else if existing := FindSubjectByName(m.SubjName); existing == nil { return m, err } else { return existing, m.MergeWith(existing) @@ -266,16 +273,16 @@ func (m *Subject) UpdateName(name string) (*Subject, error) { // UpdateMarkerNames updates related marker names. func (m *Subject) UpdateMarkerNames() error { - if m.SubjectName == "" { + if m.SubjName == "" { return fmt.Errorf("subject name is empty") - } else if m.SubjectUID == "" { + } else if m.SubjUID == "" { return fmt.Errorf("subject uid is empty") } if err := Db().Model(&Marker{}). - Where("subject_uid = ? AND subject_src <> ?", m.SubjectUID, SrcAuto). - Where("marker_name <> ?", m.SubjectName). - Update(Values{"MarkerName": m.SubjectName}).Error; err != nil { + Where("subj_uid = ? AND subj_src <> ?", m.SubjUID, SrcAuto). + Where("marker_name <> ?", m.SubjName). + Update(Values{"MarkerName": m.SubjName}).Error; err != nil { return err } @@ -284,33 +291,33 @@ func (m *Subject) UpdateMarkerNames() error { // RefreshPhotos flags related photos for metadata maintenance. func (m *Subject) RefreshPhotos() error { - if m.SubjectUID == "" { + if m.SubjUID == "" { return fmt.Errorf("empty subject uid") } return UnscopedDb().Exec(`UPDATE photos SET checked_at = NULL WHERE id IN - (SELECT f.photo_id FROM files f JOIN ? m ON m.file_uid = f.file_uid WHERE m.subject_uid = ? GROUP BY f.photo_id)`, - gorm.Expr(Marker{}.TableName()), m.SubjectUID).Error + (SELECT f.photo_id FROM files f JOIN ? m ON m.file_uid = f.file_uid WHERE m.subj_uid = ? GROUP BY f.photo_id)`, + gorm.Expr(Marker{}.TableName()), m.SubjUID).Error } // MergeWith merges this subject with another subject and then deletes it. func (m *Subject) MergeWith(other *Subject) error { if other == nil { return fmt.Errorf("other subject is nil") - } else if other.SubjectUID == "" { + } else if other.SubjUID == "" { return fmt.Errorf("other subject's uid is empty") - } else if m.SubjectUID == "" { + } else if m.SubjUID == "" { return fmt.Errorf("subject uid is empty") } - // Update markers and faces with new SubjectUID. + // Update markers and faces with new SubjUID. if err := Db().Model(&Marker{}). - Where("subject_uid = ?", m.SubjectUID). - Update(Values{"SubjectUID": other.SubjectUID}).Error; err != nil { + Where("subj_uid = ?", m.SubjUID). + Update(Values{"SubjUID": other.SubjUID}).Error; err != nil { return err } else if err := Db().Model(&Face{}). - Where("subject_uid = ?", m.SubjectUID). - Update(Values{"SubjectUID": other.SubjectUID}).Error; err != nil { + Where("subj_uid = ?", m.SubjUID). + Update(Values{"SubjUID": other.SubjUID}).Error; err != nil { return err } else if err := other.UpdateMarkerNames(); err != nil { return err @@ -321,5 +328,5 @@ func (m *Subject) MergeWith(other *Subject) error { // Links returns all share links for this entity. func (m *Subject) Links() Links { - return FindLinks("", m.SubjectUID) + return FindLinks("", m.SubjUID) } diff --git a/internal/entity/subject_fixtures.go b/internal/entity/subject_fixtures.go index ec37c9a99..5df507ccf 100644 --- a/internal/entity/subject_fixtures.go +++ b/internal/entity/subject_fixtures.go @@ -20,16 +20,16 @@ func (m SubjectMap) Pointer(name string) *Subject { var SubjectFixtures = SubjectMap{ "john-doe": Subject{ - SubjectUID: "jqu0xs11qekk9jx8", - SubjectSlug: "john-doe", - SubjectName: "John Doe", - SubjectType: SubjectPerson, - SubjectSrc: SrcManual, - Favorite: true, - Private: false, - Excluded: false, - SubjectBio: "Subject Description", - SubjectNotes: "Short Note", + SubjUID: "jqu0xs11qekk9jx8", + SubjSlug: "john-doe", + SubjName: "John Doe", + SubjType: SubjPerson, + SubjSrc: SrcManual, + SubjFavorite: true, + SubjPrivate: false, + SubjExcluded: false, + SubjBio: "Subject Description", + SubjNotes: "Short Note", MetadataJSON: []byte(""), FileCount: 1, CreatedAt: TimeStamp(), @@ -37,16 +37,16 @@ var SubjectFixtures = SubjectMap{ DeletedAt: nil, }, "joe-biden": Subject{ - SubjectUID: "jqy3y652h8njw0sx", - SubjectSlug: "joe-biden", - SubjectName: "Joe Biden", - SubjectType: SubjectPerson, - SubjectSrc: SrcMarker, - Favorite: false, - Private: false, - Excluded: false, - SubjectBio: "", - SubjectNotes: "", + SubjUID: "jqy3y652h8njw0sx", + SubjSlug: "joe-biden", + SubjName: "Joe Biden", + SubjType: SubjPerson, + SubjSrc: SrcMarker, + SubjFavorite: false, + SubjPrivate: false, + SubjExcluded: false, + SubjBio: "", + SubjNotes: "", MetadataJSON: []byte(""), FileCount: 1, CreatedAt: TimeStamp(), @@ -54,17 +54,17 @@ var SubjectFixtures = SubjectMap{ DeletedAt: nil, }, "dangling": Subject{ - SubjectUID: "jqy1y111h1njaaaa", - SubjectSlug: "dangling-subject", - SubjectName: "Dangling Subject", - SubjectAlias: "Powell", - SubjectType: SubjectPerson, - SubjectSrc: SrcMarker, - Favorite: false, - Private: false, - Excluded: false, - SubjectBio: "", - SubjectNotes: "", + SubjUID: "jqy1y111h1njaaaa", + SubjSlug: "dangling-subject", + SubjName: "Dangling Subject", + SubjAlias: "Powell", + SubjType: SubjPerson, + SubjSrc: SrcMarker, + SubjFavorite: false, + SubjPrivate: false, + SubjExcluded: false, + SubjBio: "", + SubjNotes: "", MetadataJSON: []byte(""), FileCount: 0, CreatedAt: TimeStamp(), @@ -72,16 +72,16 @@ var SubjectFixtures = SubjectMap{ DeletedAt: nil, }, "jane-doe": Subject{ - SubjectUID: "jqy1y111h1njaaab", - SubjectSlug: "jane-doe", - SubjectName: "Jane Doe", - SubjectType: SubjectPerson, - SubjectSrc: SrcMarker, - Favorite: false, - Private: false, - Excluded: false, - SubjectBio: "", - SubjectNotes: "", + SubjUID: "jqy1y111h1njaaab", + SubjSlug: "jane-doe", + SubjName: "Jane Doe", + SubjType: SubjPerson, + SubjSrc: SrcMarker, + SubjFavorite: false, + SubjPrivate: false, + SubjExcluded: false, + SubjBio: "", + SubjNotes: "", MetadataJSON: []byte(""), FileCount: 3, CreatedAt: TimeStamp(), @@ -89,28 +89,28 @@ var SubjectFixtures = SubjectMap{ DeletedAt: nil, }, "actress-1": Subject{ - SubjectUID: "jqy1y111h1njaaac", - SubjectSlug: "actress-a", - SubjectName: "Actress A", - SubjectType: SubjectPerson, - SubjectSrc: SrcMarker, - Favorite: false, - Private: false, - SubjectNotes: "", + SubjUID: "jqy1y111h1njaaac", + SubjSlug: "actress-a", + SubjName: "Actress A", + SubjType: SubjPerson, + SubjSrc: SrcMarker, + SubjFavorite: false, + SubjPrivate: false, + SubjNotes: "", MetadataJSON: []byte(""), CreatedAt: TimeStamp(), UpdatedAt: TimeStamp(), DeletedAt: nil, }, "actor-1": Subject{ - SubjectUID: "jqy1y111h1njaaad", - SubjectSlug: "actor-a", - SubjectName: "Actor A", - SubjectType: SubjectPerson, - SubjectSrc: SrcMarker, - Favorite: false, - Private: false, - SubjectNotes: "", + SubjUID: "jqy1y111h1njaaad", + SubjSlug: "actor-a", + SubjName: "Actor A", + SubjType: SubjPerson, + SubjSrc: SrcMarker, + SubjFavorite: false, + SubjPrivate: false, + SubjNotes: "", MetadataJSON: []byte(""), CreatedAt: TimeStamp(), UpdatedAt: TimeStamp(), diff --git a/internal/entity/subject_fixtures_test.go b/internal/entity/subject_fixtures_test.go index f29f710ef..fbdc9eb61 100644 --- a/internal/entity/subject_fixtures_test.go +++ b/internal/entity/subject_fixtures_test.go @@ -9,12 +9,12 @@ import ( func TestSubjectMap_Get(t *testing.T) { t.Run("get existing subject", func(t *testing.T) { r := SubjectFixtures.Get("joe-biden") - assert.Equal(t, "Joe Biden", r.SubjectName) + assert.Equal(t, "Joe Biden", r.SubjName) assert.IsType(t, Subject{}, r) }) t.Run("get not existing subject", func(t *testing.T) { r := SubjectFixtures.Get("monstera") - assert.Equal(t, "", r.SubjectName) + assert.Equal(t, "", r.SubjName) assert.IsType(t, Subject{}, r) }) } @@ -22,12 +22,12 @@ func TestSubjectMap_Get(t *testing.T) { func TestSubjectMap_Pointer(t *testing.T) { t.Run("get existing subject", func(t *testing.T) { r := SubjectFixtures.Pointer("joe-biden") - assert.Equal(t, "Joe Biden", r.SubjectName) + assert.Equal(t, "Joe Biden", r.SubjName) assert.IsType(t, &Subject{}, r) }) t.Run("get not existing subject", func(t *testing.T) { r := SubjectFixtures.Pointer("monstera") - assert.Equal(t, "", r.SubjectName) + assert.Equal(t, "", r.SubjName) assert.IsType(t, &Subject{}, r) }) } diff --git a/internal/entity/subject_person.go b/internal/entity/subject_person.go index e6f99487f..1d34c23b1 100644 --- a/internal/entity/subject_person.go +++ b/internal/entity/subject_person.go @@ -7,7 +7,7 @@ import ( ) const ( - SubjectPerson = "person" + SubjPerson = "person" ) // People represents a list of people. @@ -15,20 +15,20 @@ type People []Person // Person represents a subject with type person. type Person struct { - SubjectUID string `json:"UID"` - SubjectName string `json:"Name"` - SubjectAlias string `json:"Alias,omitempty"` - Favorite bool `json:"Favorite,omitempty"` - Thumb string `json:",omitempty"` + SubjUID string `json:"UID"` + SubjName string `json:"Name"` + SubjAlias string `json:"Alias"` + SubjFavorite bool `json:"Favorite"` + Thumb string `json:"Thumb"` } // NewPerson returns a new entity. func NewPerson(subj Subject) *Person { result := &Person{ - SubjectUID: subj.SubjectUID, - SubjectName: subj.SubjectName, - SubjectAlias: subj.SubjectAlias, - Favorite: subj.Favorite, + SubjUID: subj.SubjUID, + SubjName: subj.SubjName, + SubjAlias: subj.SubjAlias, + SubjFavorite: subj.SubjFavorite, Thumb: subj.Thumb, } @@ -44,10 +44,10 @@ func (m *Person) MarshalJSON() ([]byte, error) { Favorite bool `json:",omitempty"` Thumb string `json:",omitempty"` }{ - UID: m.SubjectUID, - Name: m.SubjectName, - Keywords: txt.NameKeywords(m.SubjectName, m.SubjectAlias), - Favorite: m.Favorite, + UID: m.SubjUID, + Name: m.SubjName, + Keywords: txt.NameKeywords(m.SubjName, m.SubjAlias), + Favorite: m.SubjFavorite, Thumb: m.Thumb, }) } diff --git a/internal/entity/subject_person_test.go b/internal/entity/subject_person_test.go index 4ea9e7520..4665639a3 100644 --- a/internal/entity/subject_person_test.go +++ b/internal/entity/subject_person_test.go @@ -9,19 +9,19 @@ import ( func TestNewPerson(t *testing.T) { t.Run("BillGates", func(t *testing.T) { subj := Subject{ - SubjectUID: "jqytw12v8jjeu3e6", - SubjectName: "William Henry Gates III", - SubjectAlias: "Windows Guru", - Favorite: true, + SubjUID: "jqytw12v8jjeu3e6", + SubjName: "William Henry Gates III", + SubjAlias: "Windows Guru", + SubjFavorite: true, Thumb: "622c7287967f2800e873fbc55f0328973056ce1d", } m := NewPerson(subj) - assert.Equal(t, "jqytw12v8jjeu3e6", m.SubjectUID) - assert.Equal(t, "William Henry Gates III", m.SubjectName) - assert.Equal(t, "Windows Guru", m.SubjectAlias) - assert.Equal(t, true, m.Favorite) + assert.Equal(t, "jqytw12v8jjeu3e6", m.SubjUID) + assert.Equal(t, "William Henry Gates III", m.SubjName) + assert.Equal(t, "Windows Guru", m.SubjAlias) + assert.Equal(t, true, m.SubjFavorite) assert.Equal(t, "622c7287967f2800e873fbc55f0328973056ce1d", m.Thumb) if j, err := m.MarshalJSON(); err != nil { diff --git a/internal/entity/subject_test.go b/internal/entity/subject_test.go index a624bedf6..5de4d3654 100644 --- a/internal/entity/subject_test.go +++ b/internal/entity/subject_test.go @@ -14,16 +14,16 @@ func TestSubject_TableName(t *testing.T) { func TestNewSubject(t *testing.T) { t.Run("Jens_Mander", func(t *testing.T) { - m := NewSubject("Jens Mander", SubjectPerson, SrcAuto) - assert.Equal(t, "Jens Mander", m.SubjectName) - assert.Equal(t, "jens-mander", m.SubjectSlug) - assert.Equal(t, "person", m.SubjectType) + m := NewSubject("Jens Mander", SubjPerson, SrcAuto) + assert.Equal(t, "Jens Mander", m.SubjName) + assert.Equal(t, "jens-mander", m.SubjSlug) + assert.Equal(t, "person", m.SubjType) }) t.Run("subject Type empty", func(t *testing.T) { m := NewSubject("Anna Mander", "", SrcAuto) - assert.Equal(t, "Anna Mander", m.SubjectName) - assert.Equal(t, "anna-mander", m.SubjectSlug) - assert.Equal(t, "person", m.SubjectType) + assert.Equal(t, "Anna Mander", m.SubjName) + assert.Equal(t, "anna-mander", m.SubjSlug) + assert.Equal(t, "person", m.SubjType) }) t.Run("subject name empty", func(t *testing.T) { m := NewSubject("", "", SrcAuto) @@ -33,44 +33,44 @@ func TestNewSubject(t *testing.T) { func TestSubject_SetName(t *testing.T) { t.Run("success", func(t *testing.T) { - m := NewSubject("Jens Mander", SubjectPerson, SrcAuto) + m := NewSubject("Jens Mander", SubjPerson, SrcAuto) - assert.Equal(t, "Jens Mander", m.SubjectName) - assert.Equal(t, "jens-mander", m.SubjectSlug) + assert.Equal(t, "Jens Mander", m.SubjName) + assert.Equal(t, "jens-mander", m.SubjSlug) if err := m.SetName("Foo McBar"); err != nil { t.Fatal(err) } - assert.Equal(t, "Foo McBar", m.SubjectName) - assert.Equal(t, "foo-mcbar", m.SubjectSlug) + assert.Equal(t, "Foo McBar", m.SubjName) + assert.Equal(t, "foo-mcbar", m.SubjSlug) }) t.Run("new name empty", func(t *testing.T) { - m := NewSubject("Jens Mander", SubjectPerson, SrcAuto) + m := NewSubject("Jens Mander", SubjPerson, SrcAuto) - assert.Equal(t, "Jens Mander", m.SubjectName) - assert.Equal(t, "jens-mander", m.SubjectSlug) + assert.Equal(t, "Jens Mander", m.SubjName) + assert.Equal(t, "jens-mander", m.SubjSlug) err := m.SetName("") if err == nil { t.Fatal(err) } assert.Equal(t, "subject: name must not be empty", err.Error()) - assert.Equal(t, "Jens Mander", m.SubjectName) + assert.Equal(t, "Jens Mander", m.SubjName) }) } func TestFirstOrCreatePerson(t *testing.T) { t.Run("not yet existing person", func(t *testing.T) { - m := NewSubject("Create Me", SubjectPerson, SrcAuto) + m := NewSubject("Create Me", SubjPerson, SrcAuto) result := FirstOrCreateSubject(m) if result == nil { t.Fatal("result should not be nil") } - assert.Equal(t, "Create Me", m.SubjectName) - assert.Equal(t, "create-me", m.SubjectSlug) + assert.Equal(t, "Create Me", m.SubjName) + assert.Equal(t, "create-me", m.SubjSlug) }) t.Run("existing person", func(t *testing.T) { m := SubjectFixtures.Pointer("john-doe") @@ -80,15 +80,15 @@ func TestFirstOrCreatePerson(t *testing.T) { t.Fatal("result should not be nil") } - assert.Equal(t, "John Doe", m.SubjectName) - assert.Equal(t, "john-doe", m.SubjectSlug) - assert.Equal(t, "Short Note", m.SubjectNotes) + assert.Equal(t, "John Doe", m.SubjName) + assert.Equal(t, "john-doe", m.SubjSlug) + assert.Equal(t, "Short Note", m.SubjNotes) }) } func TestSubject_Save(t *testing.T) { t.Run("success", func(t *testing.T) { - m := NewSubject("Save Me", SubjectPerson, SrcAuto) + m := NewSubject("Save Me", SubjPerson, SrcAuto) initialDate := m.UpdatedAt err := m.Save() @@ -105,13 +105,13 @@ func TestSubject_Save(t *testing.T) { func TestSubject_Delete(t *testing.T) { t.Run("success", func(t *testing.T) { - m := NewSubject("Jens Mander", SubjectPerson, SrcAuto) + m := NewSubject("Jens Mander", SubjPerson, SrcAuto) err := m.Save() assert.False(t, m.Deleted()) var subj Subjects - if err := Db().Where("subject_name = ?", m.SubjectName).Find(&subj).Error; err != nil { + if err := Db().Where("subj_name = ?", m.SubjName).Find(&subj).Error; err != nil { t.Fatal(err) } @@ -122,7 +122,7 @@ func TestSubject_Delete(t *testing.T) { t.Fatal(err) } - if err := Db().Where("subject_name = ?", m.SubjectName).Find(&subj).Error; err != nil { + if err := Db().Where("subj_name = ?", m.SubjName).Find(&subj).Error; err != nil { t.Fatal(err) } @@ -134,7 +134,7 @@ func TestSubject_Restore(t *testing.T) { t.Run("success", func(t *testing.T) { var deleteTime = time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) - m := &Subject{DeletedAt: &deleteTime, SubjectName: "ToBeRestored"} + m := &Subject{DeletedAt: &deleteTime, SubjName: "ToBeRestored"} err := m.Save() if err != nil { t.Fatal(err) @@ -148,7 +148,7 @@ func TestSubject_Restore(t *testing.T) { assert.False(t, m.Deleted()) }) t.Run("subject not deleted", func(t *testing.T) { - m := &Subject{DeletedAt: nil, SubjectName: "NotDeleted1234"} + m := &Subject{DeletedAt: nil, SubjName: "NotDeleted1234"} err := m.Restore() if err != nil { t.Fatal(err) @@ -159,18 +159,18 @@ func TestSubject_Restore(t *testing.T) { func TestFindSubject(t *testing.T) { t.Run("success", func(t *testing.T) { - m := NewSubject("Find Me", SubjectPerson, SrcAuto) + m := NewSubject("Find Me", SubjPerson, SrcAuto) if err := m.Save(); err != nil { t.Fatal(err) } - if s := FindSubject(m.SubjectName); s != nil { + if s := FindSubject(m.SubjName); s != nil { t.Fatal("result must be nil") } - if s := FindSubject(m.SubjectUID); s != nil { - assert.Equal(t, "Find Me", s.SubjectName) + if s := FindSubject(m.SubjUID); s != nil { + assert.Equal(t, "Find Me", s.SubjName) } else { t.Fatal("result must not be nil") } @@ -195,33 +195,33 @@ func TestSubject_Links(t *testing.T) { func TestSubject_Update(t *testing.T) { t.Run("success", func(t *testing.T) { - m := NewSubject("Update Me", SubjectPerson, SrcAuto) + m := NewSubject("Update Me", SubjPerson, SrcAuto) if err := m.Save(); err != nil { t.Fatal(err) } - if err := m.Update("SubjectName", "Updated Name"); err != nil { + if err := m.Update("SubjName", "Updated Name"); err != nil { t.Fatal(err) } else { - assert.Equal(t, "Updated Name", m.SubjectName) + assert.Equal(t, "Updated Name", m.SubjName) } }) } func TestSubject_Updates(t *testing.T) { t.Run("success", func(t *testing.T) { - m := NewSubject("Update Me", SubjectPerson, SrcAuto) + m := NewSubject("Update Me", SubjPerson, SrcAuto) if err := m.Save(); err != nil { t.Fatal(err) } - if err := m.Updates(Subject{SubjectName: "UpdatedName", SubjectType: "UpdatedType"}); err != nil { + if err := m.Updates(Subject{SubjName: "UpdatedName", SubjType: "UpdatedType"}); err != nil { t.Fatal(err) } else { - assert.Equal(t, "UpdatedName", m.SubjectName) - assert.Equal(t, "UpdatedType", m.SubjectType) + assert.Equal(t, "UpdatedName", m.SubjName) + assert.Equal(t, "UpdatedType", m.SubjType) } }) @@ -229,45 +229,45 @@ func TestSubject_Updates(t *testing.T) { func TestSubject_UpdateName(t *testing.T) { t.Run("success", func(t *testing.T) { - m := NewSubject("Test Person", SubjectPerson, SrcAuto) + m := NewSubject("Test Person", SubjPerson, SrcAuto) if err := m.Save(); err != nil { t.Fatal(err) } - assert.Equal(t, "Test Person", m.SubjectName) - assert.Equal(t, "test-person", m.SubjectSlug) + assert.Equal(t, "Test Person", m.SubjName) + assert.Equal(t, "test-person", m.SubjSlug) if s, err := m.UpdateName("New New"); err != nil { t.Fatal(err) } else if s == nil { t.Fatal("subject is nil") } else { - assert.Equal(t, "New New", m.SubjectName) - assert.Equal(t, "new-new", m.SubjectSlug) - assert.Equal(t, "New New", s.SubjectName) - assert.Equal(t, "new-new", s.SubjectSlug) + assert.Equal(t, "New New", m.SubjName) + assert.Equal(t, "new-new", m.SubjSlug) + assert.Equal(t, "New New", s.SubjName) + assert.Equal(t, "new-new", s.SubjSlug) } }) t.Run("empty name", func(t *testing.T) { - m := NewSubject("Test Person2", SubjectPerson, SrcAuto) + m := NewSubject("Test Person2", SubjPerson, SrcAuto) if err := m.Save(); err != nil { t.Fatal(err) } - assert.Equal(t, "Test Person2", m.SubjectName) - assert.Equal(t, "test-person2", m.SubjectSlug) + assert.Equal(t, "Test Person2", m.SubjName) + assert.Equal(t, "test-person2", m.SubjSlug) if s, err := m.UpdateName(""); err == nil { t.Error("error expected") } else if s == nil { t.Fatal("subject is nil") } else { - assert.Equal(t, "Test Person2", m.SubjectName) - assert.Equal(t, "test-person2", m.SubjectSlug) - assert.Equal(t, "Test Person2", s.SubjectName) - assert.Equal(t, "test-person2", s.SubjectSlug) + assert.Equal(t, "Test Person2", m.SubjName) + assert.Equal(t, "test-person2", m.SubjSlug) + assert.Equal(t, "Test Person2", s.SubjName) + assert.Equal(t, "test-person2", s.SubjSlug) } }) } diff --git a/internal/form/marker.go b/internal/form/marker.go index f31c7d11c..98fd3b283 100644 --- a/internal/form/marker.go +++ b/internal/form/marker.go @@ -4,9 +4,9 @@ import "github.com/ulule/deepcopier" // Marker represents an image marker edit form. type Marker struct { - SubjectSrc string `json:"SubjectSrc"` + SubjSrc string `json:"SubjSrc"` MarkerName string `json:"Name"` - Review bool `json:"Review"` + MarkerReview bool `json:"MarkerReview"` MarkerInvalid bool `json:"Invalid"` } diff --git a/internal/form/marker_test.go b/internal/form/marker_test.go index f3c023aff..3f5d17d5e 100644 --- a/internal/form/marker_test.go +++ b/internal/form/marker_test.go @@ -9,14 +9,14 @@ import ( func TestNewMarker(t *testing.T) { t.Run("success", func(t *testing.T) { var m = struct { - SubjectSrc string + SubjSrc string MarkerName string - Review bool + MarkerReview bool MarkerInvalid bool }{ - SubjectSrc: "manual", + SubjSrc: "manual", MarkerName: "Foo", - Review: true, + MarkerReview: true, MarkerInvalid: true, } @@ -26,9 +26,9 @@ func TestNewMarker(t *testing.T) { t.Fatal(err) } - assert.Equal(t, "manual", f.SubjectSrc) + assert.Equal(t, "manual", f.SubjSrc) assert.Equal(t, "Foo", f.MarkerName) - assert.Equal(t, true, f.Review) + assert.Equal(t, true, f.MarkerReview) assert.Equal(t, true, f.MarkerInvalid) }) } diff --git a/internal/form/selection.go b/internal/form/selection.go index e32699741..92ef810ba 100644 --- a/internal/form/selection.go +++ b/internal/form/selection.go @@ -3,11 +3,12 @@ package form import "strings" type Selection struct { - Files []string `json:"files"` - Photos []string `json:"photos"` - Albums []string `json:"albums"` - Labels []string `json:"labels"` - Places []string `json:"places"` + Files []string `json:"files"` + Photos []string `json:"photos"` + Albums []string `json:"albums"` + Labels []string `json:"labels"` + Places []string `json:"places"` + Subjects []string `json:"subjects"` } func (f Selection) Empty() bool { @@ -22,6 +23,8 @@ func (f Selection) Empty() bool { return false case len(f.Places) > 0: return false + case len(f.Subjects) > 0: + return false } return true @@ -36,6 +39,7 @@ func (f Selection) All() []string { all = append(all, f.Albums...) all = append(all, f.Labels...) all = append(all, f.Places...) + all = append(all, f.Subjects...) return all } diff --git a/internal/form/selection_test.go b/internal/form/selection_test.go index 313500d46..59d190505 100644 --- a/internal/form/selection_test.go +++ b/internal/form/selection_test.go @@ -27,6 +27,11 @@ func TestSelection_Empty(t *testing.T) { sel := Selection{Photos: []string{}, Albums: []string{}, Labels: []string{}, Files: []string{}, Places: []string{"foo", "bar"}} assert.Equal(t, false, sel.Empty()) }) + t.Run("not empty subjects", func(t *testing.T) { + sel := Selection{Photos: []string{}, Albums: []string{}, Labels: []string{}, Files: []string{}, Places: []string{}, Subjects: []string{"jqzkpo13j8ngpgv4", "jqzkq8j10hj39sxp"}} + assert.Equal(t, false, sel.Empty()) + assert.Equal(t, []string{"jqzkpo13j8ngpgv4", "jqzkq8j10hj39sxp"}, sel.Subjects) + }) t.Run("empty", func(t *testing.T) { sel := Selection{Photos: []string{}, Albums: []string{}, Labels: []string{}} assert.Equal(t, true, sel.Empty()) @@ -35,8 +40,8 @@ func TestSelection_Empty(t *testing.T) { func TestSelection_All(t *testing.T) { t.Run("success", func(t *testing.T) { - sel := Selection{Photos: []string{"p123", "p456"}, Albums: []string{"a123"}, Labels: []string{"l123", "l456", "l789"}, Files: []string{"f567", "f111"}, Places: []string{"p568"}} - assert.Equal(t, []string{"p123", "p456", "a123", "l123", "l456", "l789", "p568"}, sel.All()) + sel := Selection{Photos: []string{"p123", "p456"}, Albums: []string{"a123"}, Labels: []string{"l123", "l456", "l789"}, Files: []string{"f567", "f111"}, Places: []string{"p568"}, Subjects: []string{"jqzkpo13j8ngpgv4"}} + assert.Equal(t, []string{"p123", "p456", "a123", "l123", "l456", "l789", "p568", "jqzkpo13j8ngpgv4"}, sel.All()) }) } diff --git a/internal/form/subject.go b/internal/form/subject.go new file mode 100644 index 000000000..68f6a8ced --- /dev/null +++ b/internal/form/subject.go @@ -0,0 +1,20 @@ +package form + +import "github.com/ulule/deepcopier" + +// Subject represents an image subject edit form. +type Subject struct { + SubjName string `json:"Name"` + SubjAlias string `json:"Alias"` + SubjBio string `json:"Bio"` + SubjNotes string `json:"Notes"` + SubjFavorite bool `json:"Favorite"` + SubjPrivate bool `json:"Private"` + SubjExcluded bool `json:"Excluded"` +} + +func NewSubject(m interface{}) (f Subject, err error) { + err = deepcopier.Copy(m).To(&f) + + return f, err +} diff --git a/internal/form/subject_test.go b/internal/form/subject_test.go new file mode 100644 index 000000000..08a4b45a1 --- /dev/null +++ b/internal/form/subject_test.go @@ -0,0 +1,37 @@ +package form + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewSubject(t *testing.T) { + t.Run("success", func(t *testing.T) { + var m = struct { + SubjName string `json:"Name"` + SubjAlias string `json:"Alias"` + SubjBio string `json:"Bio"` + SubjNotes string `json:"Notes"` + SubjFavorite bool `json:"Favorite"` + SubjPrivate bool `json:"Private"` + SubjExcluded bool `json:"Excluded"` + }{ + SubjName: "Foo", + SubjAlias: "bar", + SubjFavorite: true, + SubjExcluded: false, + } + + f, err := NewSubject(m) + + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, "Foo", f.SubjName) + assert.Equal(t, "bar", f.SubjAlias) + assert.Equal(t, true, f.SubjFavorite) + assert.Equal(t, false, f.SubjExcluded) + }) +} diff --git a/internal/i18n/messages.go b/internal/i18n/messages.go index e664a70c8..2ca429169 100644 --- a/internal/i18n/messages.go +++ b/internal/i18n/messages.go @@ -60,6 +60,8 @@ const ( MsgCopyingFilesFrom MsgLabelsDeleted MsgLabelSaved + MsgSubjectSaved + MsgSubjectDeleted MsgFilesUploadedIn MsgSelectionApproved MsgSelectionArchived @@ -132,6 +134,8 @@ var Messages = MessageMap{ MsgCopyingFilesFrom: gettext("Copying files from %s"), MsgLabelsDeleted: gettext("Labels deleted"), MsgLabelSaved: gettext("Label saved"), + MsgSubjectSaved: gettext("Subject saved"), + MsgSubjectDeleted: gettext("Subject deleted"), MsgFilesUploadedIn: gettext("%d files uploaded in %d s"), MsgSelectionApproved: gettext("Selection approved"), MsgSelectionArchived: gettext("Selection archived"), diff --git a/internal/photoprism/faces_audit.go b/internal/photoprism/faces_audit.go index affa987e9..8b0e49b6e 100644 --- a/internal/photoprism/faces_audit.go +++ b/internal/photoprism/faces_audit.go @@ -65,7 +65,7 @@ func (w *Faces) Audit(fix bool) (err error) { for _, f2 := range faces { if matched, dist := f1.Match(entity.Embeddings{f2.Embedding()}); matched { - if f1.SubjectUID == f2.SubjectUID { + if f1.SubjUID == f2.SubjUID { continue } @@ -75,14 +75,14 @@ func (w *Faces) Audit(fix bool) (err error) { log.Infof("face %s: conflict at dist %f, Ø %f from %d samples, collision Ø %f", f1.ID, dist, r, f1.Samples, f1.CollisionRadius) - if f1.SubjectUID != "" { - log.Infof("face %s: subject %s (%s %s)", f1.ID, txt.Quote(subj[f1.SubjectUID].SubjectName), f1.SubjectUID, entity.SrcString(f1.FaceSrc)) + if f1.SubjUID != "" { + log.Infof("face %s: subject %s (%s %s)", f1.ID, txt.Quote(subj[f1.SubjUID].SubjName), f1.SubjUID, entity.SrcString(f1.FaceSrc)) } else { log.Infof("face %s: no subject (%s)", f1.ID, entity.SrcString(f1.FaceSrc)) } - if f2.SubjectUID != "" { - log.Infof("face %s: subject %s (%s %s)", f2.ID, txt.Quote(subj[f2.SubjectUID].SubjectName), f2.SubjectUID, entity.SrcString(f2.FaceSrc)) + if f2.SubjUID != "" { + log.Infof("face %s: subject %s (%s %s)", f2.ID, txt.Quote(subj[f2.SubjUID].SubjName), f2.SubjUID, entity.SrcString(f2.FaceSrc)) } else { log.Infof("face %s: no subject (%s)", f2.ID, entity.SrcString(f2.FaceSrc)) } @@ -113,7 +113,7 @@ func (w *Faces) Audit(fix bool) (err error) { log.Error(err) } else { for _, m := range markers { - log.Infof("marker %s: %s subject %s conflicts with face %s subject %s", m.MarkerUID, entity.SrcString(m.SubjectSrc), txt.Quote(subj[m.SubjectUID].SubjectName), m.FaceID, txt.Quote(subj[faceMap[m.FaceID].SubjectUID].SubjectName)) + log.Infof("marker %s: %s subject %s conflicts with face %s subject %s", m.MarkerUID, entity.SrcString(m.SubjSrc), txt.Quote(subj[m.SubjUID].SubjName), m.FaceID, txt.Quote(subj[faceMap[m.FaceID].SubjUID].SubjName)) } } diff --git a/internal/photoprism/faces_match.go b/internal/photoprism/faces_match.go index 4dc20dc61..faebd2989 100644 --- a/internal/photoprism/faces_match.go +++ b/internal/photoprism/faces_match.go @@ -167,7 +167,7 @@ func (w *Faces) MatchFaces(faces entity.Faces, force bool, matchedBefore *time.T result.Updated++ } - if marker.SubjectUID != "" { + if marker.SubjUID != "" { result.Recognized++ } else { result.Unknown++ diff --git a/internal/photoprism/faces_optimize.go b/internal/photoprism/faces_optimize.go index 73fab4ad7..da3c8791e 100644 --- a/internal/photoprism/faces_optimize.go +++ b/internal/photoprism/faces_optimize.go @@ -37,7 +37,7 @@ func (w *Faces) Optimize() (result FacesOptimizeResult, err error) { for j := 0; j <= n; j++ { if len(merge) == 0 { merge = entity.Faces{faces[j]} - } else if faces[j].SubjectUID != merge[len(merge)-1].SubjectUID || j == n { + } else if faces[j].SubjUID != merge[len(merge)-1].SubjUID || j == n { if len(merge) < 2 { // Nothing to merge. } else if _, err := query.MergeFaces(merge); err != nil { @@ -48,7 +48,7 @@ func (w *Faces) Optimize() (result FacesOptimizeResult, err error) { merge = nil } else if ok, dist := merge[0].Match(entity.Embeddings{faces[j].Embedding()}); ok { - log.Debugf("faces: can merge %s with %s, subject %s, dist %f", merge[0].ID, faces[j].ID, merge[0].SubjectUID, dist) + log.Debugf("faces: can merge %s with %s, subject %s, dist %f", merge[0].ID, faces[j].ID, merge[0].SubjUID, dist) merge = append(merge, faces[j]) } else if len(merge) == 1 { merge = nil diff --git a/internal/photoprism/faces_stats.go b/internal/photoprism/faces_stats.go index 2f7cc47de..8093c1548 100644 --- a/internal/photoprism/faces_stats.go +++ b/internal/photoprism/faces_stats.go @@ -69,7 +69,7 @@ func (w *Faces) Stats() (err error) { min := -1.0 max := -1.0 - if k, ok := dist[f1.SubjectUID]; ok { + if k, ok := dist[f1.SubjUID]; ok { min = k[0] max = k[1] } @@ -81,7 +81,7 @@ func (w *Faces) Stats() (err error) { f2 := faces[j] - if f1.SubjectUID != f2.SubjectUID { + if f1.SubjUID != f2.SubjUID { continue } @@ -99,7 +99,7 @@ func (w *Faces) Stats() (err error) { } if max > 0 { - dist[f1.SubjectUID] = []float64{min, max} + dist[f1.SubjUID] = []float64{min, max} } } diff --git a/internal/query/faces.go b/internal/query/faces.go index 38e58cbf9..9525b2007 100644 --- a/internal/query/faces.go +++ b/internal/query/faces.go @@ -19,10 +19,10 @@ func Faces(knownOnly, unmatched bool) (result entity.Faces, err error) { } if knownOnly { - stmt = stmt.Where("subject_uid <> ''") + stmt = stmt.Where("subj_uid <> ''") } - err = stmt.Order("subject_uid, samples DESC").Find(&result).Error + err = stmt.Order("subj_uid, samples DESC").Find(&result).Error return result, err } @@ -31,7 +31,7 @@ func Faces(knownOnly, unmatched bool) (result entity.Faces, err error) { func ManuallyAddedFaces() (result entity.Faces, err error) { err = Db(). Where("face_src = ?", entity.SrcManual). - Where("subject_uid <> ''").Order("subject_uid, samples DESC"). + Where("subj_uid <> ''").Order("subj_uid, samples DESC"). Find(&result).Error return result, err @@ -48,9 +48,9 @@ func MatchFaceMarkers() (affected int64, err error) { for _, f := range faces { if res := Db().Model(&entity.Marker{}). Where("face_id = ?", f.ID). - Where("subject_src = ?", entity.SrcAuto). - Where("subject_uid <> ?", f.SubjectUID). - Updates(entity.Values{"SubjectUID": f.SubjectUID, "Review": false}); res.Error != nil { + Where("subj_src = ?", entity.SrcAuto). + Where("subj_uid <> ?", f.SubjUID). + Updates(entity.Values{"SubjUID": f.SubjUID, "MarkerReview": false}); res.Error != nil { return affected, err } else if res.RowsAffected > 0 { affected += res.RowsAffected @@ -64,7 +64,7 @@ func MatchFaceMarkers() (affected int64, err error) { func RemoveAnonymousFaceClusters() (removed int64, err error) { res := UnscopedDb().Delete( entity.Face{}, - "face_src = ? AND subject_uid = ''", entity.SrcAuto) + "face_src = ? AND subj_uid = ''", entity.SrcAuto) return res.RowsAffected, res.Error } @@ -131,20 +131,20 @@ func MergeFaces(merge entity.Faces) (merged *entity.Face, err error) { return merged, fmt.Errorf("faces: two or more clusters required for merging") } - subjectUID := merge[0].SubjectUID + subjUID := merge[0].SubjUID for i := 1; i < len(merge); i++ { - if merge[i].SubjectUID != subjectUID { + if merge[i].SubjUID != subjUID { return merged, fmt.Errorf("faces: can't merge clusters with conflicting subjects %s <> %s", - txt.Quote(subjectUID), txt.Quote(merge[i].SubjectUID)) + txt.Quote(subjUID), txt.Quote(merge[i].SubjUID)) } } // Find or create merged face cluster. - if merged = entity.NewFace(merge[0].SubjectUID, merge[0].FaceSrc, merge.Embeddings()); merged == nil { - return merged, fmt.Errorf("faces: new cluster is nil for subject %s", txt.Quote(subjectUID)) + if merged = entity.NewFace(merge[0].SubjUID, merge[0].FaceSrc, merge.Embeddings()); merged == nil { + return merged, fmt.Errorf("faces: new cluster is nil for subject %s", txt.Quote(subjUID)) } else if merged = entity.FirstOrCreateFace(merged); merged == nil { - return merged, fmt.Errorf("faces: failed creating new cluster for subject %s", txt.Quote(subjectUID)) + return merged, fmt.Errorf("faces: failed creating new cluster for subject %s", txt.Quote(subjUID)) } else if err := merged.MatchMarkers(append(merge.IDs(), "")); err != nil { return merged, err } @@ -153,9 +153,9 @@ func MergeFaces(merge entity.Faces) (merged *entity.Face, err error) { if removed, err := PurgeOrphanFaces(merge.IDs()); err != nil { return merged, err } else if removed > 0 { - log.Debugf("faces: removed %d orphans for subject %s", removed, txt.Quote(subjectUID)) + log.Debugf("faces: removed %d orphans for subject %s", removed, txt.Quote(subjUID)) } else { - log.Warnf("faces: failed removing merged clusters for subject %s", txt.Quote(subjectUID)) + log.Warnf("faces: failed removing merged clusters for subject %s", txt.Quote(subjUID)) } return merged, err @@ -172,7 +172,7 @@ func ResolveFaceCollisions() (conflicts, resolved int, err error) { for _, f1 := range faces { for _, f2 := range faces { if matched, dist := f1.Match(entity.Embeddings{f2.Embedding()}); matched { - if f1.SubjectUID == f2.SubjectUID { + if f1.SubjUID == f2.SubjUID { continue } @@ -182,14 +182,14 @@ func ResolveFaceCollisions() (conflicts, resolved int, err error) { log.Infof("face %s: conflict at dist %f, Ø %f from %d samples, collision Ø %f", f1.ID, dist, r, f1.Samples, f1.CollisionRadius) - if f1.SubjectUID != "" { - log.Debugf("face %s: subject %s (%s %s)", f1.ID, txt.Quote(f1.SubjectUID), f1.SubjectUID, entity.SrcString(f1.FaceSrc)) + if f1.SubjUID != "" { + log.Debugf("face %s: subject %s (%s %s)", f1.ID, txt.Quote(f1.SubjUID), f1.SubjUID, entity.SrcString(f1.FaceSrc)) } else { log.Debugf("face %s: no subject (%s)", f1.ID, entity.SrcString(f1.FaceSrc)) } - if f2.SubjectUID != "" { - log.Debugf("face %s: subject %s (%s %s)", f2.ID, txt.Quote(f2.SubjectUID), f2.SubjectUID, entity.SrcString(f2.FaceSrc)) + if f2.SubjUID != "" { + log.Debugf("face %s: subject %s (%s %s)", f2.ID, txt.Quote(f2.SubjUID), f2.SubjUID, entity.SrcString(f2.FaceSrc)) } else { log.Debugf("face %s: no subject (%s)", f2.ID, entity.SrcString(f2.FaceSrc)) } diff --git a/internal/query/faces_test.go b/internal/query/faces_test.go index e4b70528d..6ff5b4b2e 100644 --- a/internal/query/faces_test.go +++ b/internal/query/faces_test.go @@ -62,14 +62,14 @@ func TestMatchFaceMarkers(t *testing.T) { } else if m == nil { t.Fatal("marker is nil") } else { - assert.Empty(t, m.SubjectUID) + assert.Empty(t, m.SubjUID) } - // Reset subject_uid. + // Reset subj_uid. if err := Db().Model(&entity.Marker{}). - Where("subject_src = ?", entity.SrcAuto). - Where("subject_uid = ?", "jqu0xs11qekk9jx8"). - Updates(entity.Values{"SubjectUID": ""}).Error; err != nil { + Where("subj_src = ?", entity.SrcAuto). + Where("subj_uid = ?", "jqu0xs11qekk9jx8"). + Updates(entity.Values{"SubjUID": ""}).Error; err != nil { t.Fatal(err) } @@ -86,7 +86,7 @@ func TestMatchFaceMarkers(t *testing.T) { } else if m == nil { t.Fatal("marker is nil") } else { - assert.Equal(t, "jqu0xs11qekk9jx8", m.SubjectUID) + assert.Equal(t, "jqu0xs11qekk9jx8", m.SubjUID) } } @@ -152,7 +152,7 @@ func TestMergeFaces(t *testing.T) { assert.Equal(t, "5LH5E35ZGUMF5AYLM42BIZH4DGQHJDAV", result.ID) assert.Equal(t, entity.SrcManual, result.FaceSrc) - assert.Equal(t, "jqynvsf28rhn6b0c", result.SubjectUID) + assert.Equal(t, "jqynvsf28rhn6b0c", result.SubjUID) assert.Equal(t, 2, result.Samples) assert.Equal(t, 0.03948165743305488, result.SampleRadius) assert.Equal(t, 0, result.Collisions) diff --git a/internal/query/geo.go b/internal/query/geo.go index 50a86de34..d89e964ad 100644 --- a/internal/query/geo.go +++ b/internal/query/geo.go @@ -44,7 +44,7 @@ func Geo(f form.GeoSearch) (results GeoResults, err error) { // Modify query if it contains subject names. if f.Query != "" && f.Subject == "" { - if subj, names, remaining := SearchSubjectUIDs(f.Query); len(subj) > 0 { + if subj, names, remaining := SearchSubjUIDs(f.Query); len(subj) > 0 { f.Subject = strings.Join(subj, And) log.Debugf("search: subject %s", txt.Quote(strings.Join(names, ", "))) f.Query = remaining @@ -115,12 +115,12 @@ func Geo(f form.GeoSearch) (results GeoResults, err error) { // Filter for one or more subjects? if f.Subject != "" { for _, subj := range strings.Split(strings.ToLower(f.Subject), And) { - s = s.Where(fmt.Sprintf("photos.id IN (SELECT photo_id FROM files f JOIN %s m ON f.file_uid = m.file_uid AND m.marker_invalid = 0 WHERE subject_uid IN (?))", + s = s.Where(fmt.Sprintf("photos.id IN (SELECT photo_id FROM files f JOIN %s m ON f.file_uid = m.file_uid AND m.marker_invalid = 0 WHERE subj_uid IN (?))", entity.Marker{}.TableName()), strings.Split(subj, Or)) } } else if f.Subjects != "" { - for _, where := range LikeAnyWord("s.subject_name", f.Subjects) { - s = s.Where(fmt.Sprintf("photos.id IN (SELECT photo_id FROM files f JOIN %s m ON f.file_uid = m.file_uid AND m.marker_invalid = 0 JOIN %s s ON s.subject_uid = m.subject_uid WHERE (?))", + for _, where := range LikeAnyWord("s.subj_name", f.Subjects) { + s = s.Where(fmt.Sprintf("photos.id IN (SELECT photo_id FROM files f JOIN %s m ON f.file_uid = m.file_uid AND m.marker_invalid = 0 JOIN %s s ON s.subj_uid = m.subj_uid WHERE (?))", entity.Marker{}.TableName(), entity.Subject{}.TableName()), gorm.Expr(where)) } } diff --git a/internal/query/markers.go b/internal/query/markers.go index 11c6e9f39..ec0b595be 100644 --- a/internal/query/markers.go +++ b/internal/query/markers.go @@ -29,7 +29,7 @@ func Markers(limit, offset int, markerType string, embeddings, subjects bool, ma } if subjects { - db = db.Where("subject_uid <> ''") + db = db.Where("subj_uid <> ''") } if !matchedBefore.IsZero() { @@ -119,8 +119,8 @@ func Embeddings(single, unclustered bool, size, score int) (result entity.Embedd func RemoveInvalidMarkerReferences() (removed int64, err error) { res := Db(). Model(&entity.Marker{}). - Where("marker_invalid = 1 AND (subject_uid <> '' OR face_id <> '')"). - UpdateColumns(entity.Values{"subject_uid": "", "face_id": "", "face_dist": -1.0, "matched_at": nil}) + Where("marker_invalid = 1 AND (subj_uid <> '' OR face_id <> '')"). + UpdateColumns(entity.Values{"subj_uid": "", "face_id": "", "face_dist": -1.0, "matched_at": nil}) return res.RowsAffected, res.Error } @@ -141,8 +141,8 @@ func RemoveNonExistentMarkerFaces() (removed int64, err error) { func RemoveNonExistentMarkerSubjects() (removed int64, err error) { res := Db(). Model(&entity.Marker{}). - Where(fmt.Sprintf("subject_uid <> '' AND subject_uid NOT IN (SELECT subject_uid FROM %s)", entity.Subject{}.TableName())). - UpdateColumns(entity.Values{"subject_uid": "", "matched_at": nil}) + Where(fmt.Sprintf("subj_uid <> '' AND subj_uid NOT IN (SELECT subj_uid FROM %s)", entity.Subject{}.TableName())). + UpdateColumns(entity.Values{"subj_uid": "", "matched_at": nil}) return res.RowsAffected, res.Error } @@ -182,7 +182,7 @@ func MarkersWithNonExistentReferences() (faces entity.Markers, subjects entity.M // Find markers with invalid subject UIDs. if res := Db(). - Where(fmt.Sprintf("subject_uid <> '' AND subject_uid NOT IN (SELECT subject_uid FROM %s)", entity.Subject{}.TableName())). + Where(fmt.Sprintf("subj_uid <> '' AND subj_uid NOT IN (SELECT subj_uid FROM %s)", entity.Subject{}.TableName())). Find(&subjects); res.Error != nil { err = res.Error } @@ -193,7 +193,7 @@ func MarkersWithNonExistentReferences() (faces entity.Markers, subjects entity.M // MarkersWithSubjectConflict finds markers with conflicting subjects. func MarkersWithSubjectConflict() (results entity.Markers, err error) { err = Db(). - Joins(fmt.Sprintf("JOIN %s f ON f.id = face_id AND f.subject_uid <> %s.subject_uid", entity.Face{}.TableName(), entity.Marker{}.TableName())). + Joins(fmt.Sprintf("JOIN %s f ON f.id = face_id AND f.subj_uid <> %s.subj_uid", entity.Face{}.TableName(), entity.Marker{}.TableName())). Order("face_id"). Find(&results).Error @@ -203,8 +203,8 @@ func MarkersWithSubjectConflict() (results entity.Markers, err error) { // ResetFaceMarkerMatches removes automatically added subject and face references from the markers table. func ResetFaceMarkerMatches() (removed int64, err error) { res := Db().Model(&entity.Marker{}). - Where("subject_src = ? AND marker_type = ?", entity.SrcAuto, entity.MarkerFace). - UpdateColumns(entity.Values{"marker_name": "", "subject_uid": "", "subject_src": "", "face_id": "", "face_dist": -1.0, "matched_at": nil}) + Where("subj_src = ? AND marker_type = ?", entity.SrcAuto, entity.MarkerFace). + UpdateColumns(entity.Values{"marker_name": "", "subj_uid": "", "subj_src": "", "face_id": "", "face_dist": -1.0, "matched_at": nil}) return res.RowsAffected, res.Error } diff --git a/internal/query/photo_search.go b/internal/query/photo_search.go index 01ed51fa4..56383604a 100644 --- a/internal/query/photo_search.go +++ b/internal/query/photo_search.go @@ -137,7 +137,7 @@ func PhotoSearch(f form.PhotoSearch) (results PhotoResults, count int, err error // Modify query if it contains subject names. if f.Query != "" && f.Subject == "" { - if subj, names, remaining := SearchSubjectUIDs(f.Query); len(subj) > 0 { + if subj, names, remaining := SearchSubjUIDs(f.Query); len(subj) > 0 { f.Subject = strings.Join(subj, And) log.Debugf("people: searching for %s", txt.Quote(txt.JoinNames(names))) f.Query = remaining @@ -222,12 +222,12 @@ func PhotoSearch(f form.PhotoSearch) (results PhotoResults, count int, err error // Filter for one or more subjects? if f.Subject != "" { for _, subj := range strings.Split(strings.ToLower(f.Subject), And) { - s = s.Where(fmt.Sprintf("photos.id IN (SELECT photo_id FROM files f JOIN %s m ON f.file_uid = m.file_uid AND m.marker_invalid = 0 WHERE subject_uid IN (?))", + s = s.Where(fmt.Sprintf("photos.id IN (SELECT photo_id FROM files f JOIN %s m ON f.file_uid = m.file_uid AND m.marker_invalid = 0 WHERE subj_uid IN (?))", entity.Marker{}.TableName()), strings.Split(subj, Or)) } } else if f.Subjects != "" { - for _, where := range LikeAnyWord("s.subject_name", f.Subjects) { - s = s.Where(fmt.Sprintf("photos.id IN (SELECT photo_id FROM files f JOIN %s m ON f.file_uid = m.file_uid AND m.marker_invalid = 0 JOIN %s s ON s.subject_uid = m.subject_uid WHERE (?))", + for _, where := range LikeAnyWord("s.subj_name", f.Subjects) { + s = s.Where(fmt.Sprintf("photos.id IN (SELECT photo_id FROM files f JOIN %s m ON f.file_uid = m.file_uid AND m.marker_invalid = 0 JOIN %s s ON s.subj_uid = m.subj_uid WHERE (?))", entity.Marker{}.TableName(), entity.Subject{}.TableName()), gorm.Expr(where)) } } diff --git a/internal/query/previews.go b/internal/query/previews.go index 02f82bd9d..481c048b8 100644 --- a/internal/query/previews.go +++ b/internal/query/previews.go @@ -159,7 +159,7 @@ func UpdateSubjectPreviews() (err error) { return Db().Table(entity.Subject{}.TableName()). UpdateColumn("thumb", gorm.Expr("(SELECT f.file_hash FROM files f "+ fmt.Sprintf( - "JOIN %s m ON f.file_uid = m.file_uid AND m.subject_uid = %s.subject_uid", + "JOIN %s m ON f.file_uid = m.file_uid AND m.subj_uid = %s.subj_uid", entity.Marker{}.TableName(), entity.Subject{}.TableName())+ ` JOIN photos p ON f.photo_id = p.id @@ -172,7 +172,7 @@ func UpdateSubjectPreviews() (err error) { err = Db().Table(entity.Subject{}.TableName()). UpdateColumn("thumb", gorm.Expr("(SELECT m.file_hash FROM "+ fmt.Sprintf( - "%s m WHERE m.subject_uid = %s.subject_uid AND m.subject_src = 'manual' ", + "%s m WHERE m.subj_uid = %s.subj_uid AND m.subj_src = 'manual' ", entity.Marker{}.TableName(), entity.Subject{}.TableName())+ ` AND m.file_hash <> '' ORDER BY m.size DESC LIMIT 1) diff --git a/internal/query/selection.go b/internal/query/selection.go index a68928493..58ef8cb30 100644 --- a/internal/query/selection.go +++ b/internal/query/selection.go @@ -32,13 +32,14 @@ func PhotoSelection(f form.Selection) (results entity.Photos, err error) { SELECT a.path FROM folders a WHERE a.folder_uid IN (?) UNION SELECT b.path FROM folders a JOIN folders b ON b.path LIKE %s WHERE a.folder_uid IN (?)) OR photos.photo_uid IN (SELECT photo_uid FROM photos_albums WHERE hidden = 0 AND album_uid IN (?)) + OR photos.id IN (SELECT f.photo_id FROM files f JOIN %s m ON f.file_uid = m.file_uid WHERE f.deleted_at IS NULL AND m.subj_uid IN (?)) OR photos.id IN (SELECT pl.photo_id FROM photos_labels pl JOIN labels l ON pl.label_id = l.id AND l.deleted_at IS NULL WHERE l.label_uid IN (?)) OR photos.id IN (SELECT pl.photo_id FROM photos_labels pl JOIN categories c ON c.label_id = pl.label_id JOIN labels lc ON lc.id = c.category_id AND lc.deleted_at IS NULL WHERE lc.label_uid IN (?))`, - concat) + concat, entity.Marker{}.TableName()) s := UnscopedDb().Table("photos"). Select("photos.*"). - Where(where, f.Photos, f.Places, f.Files, f.Files, f.Files, f.Albums, f.Labels, f.Labels) + Where(where, f.Photos, f.Places, f.Files, f.Files, f.Files, f.Albums, f.Subjects, f.Labels, f.Labels) if result := s.Scan(&results); result.Error != nil { return results, result.Error @@ -71,16 +72,17 @@ func FileSelection(f form.Selection) (results entity.Files, err error) { SELECT a.path FROM folders a WHERE a.folder_uid IN (?) UNION SELECT b.path FROM folders a JOIN folders b ON b.path LIKE %s WHERE a.folder_uid IN (?)) OR photos.photo_uid IN (SELECT photo_uid FROM photos_albums WHERE hidden = 0 AND album_uid IN (?)) + OR files.file_uid IN (SELECT file_uid FROM %s m WHERE m.subj_uid IN (?)) OR photos.id IN (SELECT pl.photo_id FROM photos_labels pl JOIN labels l ON pl.label_id = l.id AND l.deleted_at IS NULL WHERE l.label_uid IN (?)) OR photos.id IN (SELECT pl.photo_id FROM photos_labels pl JOIN categories c ON c.label_id = pl.label_id JOIN labels lc ON lc.id = c.category_id AND lc.deleted_at IS NULL WHERE lc.label_uid IN (?))`, - concat) + concat, entity.Marker{}.TableName()) s := UnscopedDb().Table("files"). Select("files.*"). Joins("JOIN photos ON photos.id = files.photo_id"). Where("photos.deleted_at IS NULL"). Where("files.file_missing = 0"). - Where(where, f.Photos, f.Places, f.Files, f.Files, f.Files, f.Albums, f.Labels, f.Labels). + Where(where, f.Photos, f.Places, f.Files, f.Files, f.Files, f.Albums, f.Subjects, f.Labels, f.Labels). Group("files.id") if result := s.Scan(&results); result.Error != nil { diff --git a/internal/query/subject_search.go b/internal/query/subject_search.go index 9f0e52912..7eb3a39b8 100644 --- a/internal/query/subject_search.go +++ b/internal/query/subject_search.go @@ -13,16 +13,16 @@ import ( // SubjectResult represents a subject search result. type SubjectResult struct { - SubjectUID string `json:"UID"` - SubjectType string `json:"Type"` - SubjectSlug string `json:"Slug"` - SubjectName string `json:"Name"` - SubjectAlias string `json:"Alias,omitempty"` - Thumb string `json:"Thumb,omitempty"` - Favorite bool `json:"Favorite,omitempty"` - Private bool `json:"Private,omitempty"` - Excluded bool `json:"Excluded,omitempty"` - FileCount int `json:"Files,omitempty"` + SubjUID string `json:"UID"` + SubjType string `json:"Type"` + SubjSlug string `json:"Slug"` + SubjName string `json:"Name"` + SubjAlias string `json:"Alias"` + SubjFavorite bool `json:"Favorite"` + SubjPrivate bool `json:"Private"` + SubjExcluded bool `json:"Excluded"` + FileCount int `json:"Files"` + Thumb string `json:"Thumb"` } // SubjectResults represents subject search results. @@ -38,7 +38,7 @@ func SubjectSearch(f form.SubjectSearch) (results SubjectResults, err error) { // Base query. s := UnscopedDb().Table(entity.Subject{}.TableName()). - Select("subject_uid, subject_slug, subject_name, subject_alias, subject_type, thumb, favorite, private, excluded, file_count") + Select("subj_uid, subj_slug, subj_name, subj_alias, subj_type, thumb, subj_favorite, subj_private, subj_excluded, file_count") // Limit result count. if f.Count > 0 && f.Count <= MaxResults { @@ -49,14 +49,20 @@ func SubjectSearch(f form.SubjectSearch) (results SubjectResults, err error) { // Set sort order. switch f.Order { + case "name": + s = s.Order("subj_name") case "count": s = s.Order("file_count DESC") + case "added": + s = s.Order("created_at DESC") + case "relevance": + s = s.Order("subj_favorite DESC, subj_name") default: - s = s.Order("subject_name") + s = s.Order("subj_favorite DESC, subj_name") } if f.ID != "" { - s = s.Where("subject_uid IN (?)", strings.Split(f.ID, Or)) + s = s.Where("subj_uid IN (?)", strings.Split(f.ID, Or)) if result := s.Scan(&results); result.Error != nil { return results, result.Error @@ -66,27 +72,28 @@ func SubjectSearch(f form.SubjectSearch) (results SubjectResults, err error) { } if f.Query != "" { - for _, where := range LikeAnyWord("subject_name", f.Query) { + for _, where := range LikeAnyWord("subj_name", f.Query) { s = s.Where("(?)", gorm.Expr(where)) } } if f.Type != "" { - s = s.Where("subject_type IN (?)", strings.Split(f.Type, Or)) + s = s.Where("subj_type IN (?)", strings.Split(f.Type, Or)) } if f.Favorite { - s = s.Where("favorite = 1") + s = s.Where("subj_favorite = 1") } if f.Private { - s = s.Where("private = 1") + s = s.Where("subj_private = 1") } if f.Excluded { - s = s.Where("excluded = 1") + s = s.Where("subj_excluded = 1") } + // Omit deleted rows. s = s.Where("deleted_at IS NULL") if result := s.Scan(&results); result.Error != nil { diff --git a/internal/query/subject_search_test.go b/internal/query/subject_search_test.go index a7b322970..9c9d7b3af 100644 --- a/internal/query/subject_search_test.go +++ b/internal/query/subject_search_test.go @@ -12,7 +12,7 @@ import ( func TestSubjectSearch(t *testing.T) { t.Run("FindAll", func(t *testing.T) { - results, err := SubjectSearch(form.SubjectSearch{Type: entity.SubjectPerson}) + results, err := SubjectSearch(form.SubjectSearch{Type: entity.SubjPerson}) assert.NoError(t, err) assert.LessOrEqual(t, 3, len(results)) }) diff --git a/internal/query/subjects.go b/internal/query/subjects.go index c8ef12a60..8078c18e6 100644 --- a/internal/query/subjects.go +++ b/internal/query/subjects.go @@ -14,9 +14,9 @@ import ( func People() (people entity.People, err error) { err = UnscopedDb(). Table(entity.Subject{}.TableName()). - Select("subject_uid, subject_name, subject_alias, favorite "). - Where("deleted_at IS NULL AND subject_type = ?", entity.SubjectPerson). - Order("favorite, subject_name"). + Select("subj_uid, subj_name, subj_alias, subj_favorite "). + Where("deleted_at IS NULL AND subj_type = ?", entity.SubjPerson). + Order("subj_favorite, subj_name"). Limit(2000).Offset(0). Scan(&people).Error @@ -28,7 +28,7 @@ func PeopleCount() (count int, err error) { err = Db(). Table(entity.Subject{}.TableName()). Where("deleted_at IS NULL"). - Where("subject_type = ?", entity.SubjectPerson). + Where("subj_type = ?", entity.SubjPerson). Count(&count).Error return count, err @@ -38,7 +38,7 @@ func PeopleCount() (count int, err error) { func Subjects(limit, offset int) (result entity.Subjects, err error) { stmt := Db() - stmt = stmt.Order("subject_name").Limit(limit).Offset(offset) + stmt = stmt.Order("subj_name").Limit(limit).Offset(offset) err = stmt.Find(&result).Error return result, err @@ -57,7 +57,7 @@ func SubjectMap() (result map[string]entity.Subject, err error) { } for _, s := range subj { - result[s.SubjectUID] = s + result[s.SubjUID] = s } return result, err @@ -66,9 +66,9 @@ func SubjectMap() (result map[string]entity.Subject, err error) { // RemoveDanglingMarkerSubjects permanently deletes dangling marker subjects from the index. func RemoveDanglingMarkerSubjects() (removed int64, err error) { res := UnscopedDb(). - Where("subject_src = ?", entity.SrcMarker). - Where(fmt.Sprintf("subject_uid NOT IN (SELECT subject_uid FROM %s)", entity.Face{}.TableName())). - Where(fmt.Sprintf("subject_uid NOT IN (SELECT subject_uid FROM %s)", entity.Marker{}.TableName())). + Where("subj_src = ?", entity.SrcMarker). + Where(fmt.Sprintf("subj_uid NOT IN (SELECT subj_uid FROM %s)", entity.Face{}.TableName())). + Where(fmt.Sprintf("subj_uid NOT IN (SELECT subj_uid FROM %s)", entity.Marker{}.TableName())). Delete(&entity.Subject{}) return res.RowsAffected, res.Error @@ -79,7 +79,7 @@ func CreateMarkerSubjects() (affected int64, err error) { var markers entity.Markers if err := Db(). - Where("subject_uid = '' AND marker_name <> '' AND subject_src <> ?", entity.SrcAuto). + Where("subj_uid = '' AND marker_name <> '' AND subj_src <> ?", entity.SrcAuto). Where("marker_invalid = 0 AND marker_type = ?", entity.MarkerFace). Order("marker_name"). Find(&markers).Error; err != nil { @@ -94,7 +94,7 @@ func CreateMarkerSubjects() (affected int64, err error) { for _, m := range markers { if name == m.MarkerName && subj != nil { // Do nothing. - } else if subj = entity.NewSubject(m.MarkerName, entity.SubjectPerson, entity.SrcMarker); subj == nil { + } else if subj = entity.NewSubject(m.MarkerName, entity.SubjPerson, entity.SrcMarker); subj == nil { log.Errorf("faces: subject should not be nil - bug?") continue } else if subj = entity.FirstOrCreateSubject(subj); subj == nil { @@ -106,13 +106,13 @@ func CreateMarkerSubjects() (affected int64, err error) { name = m.MarkerName - if err := m.Updates(entity.Values{"SubjectUID": subj.SubjectUID, "Review": false}); err != nil { + if err := m.Updates(entity.Values{"SubjUID": subj.SubjUID, "MarkerReview": false}); err != nil { return affected, err } if m.FaceID == "" { continue - } else if err := Db().Model(&entity.Face{}).Where("id = ? AND subject_uid = ''", m.FaceID).Update("SubjectUID", subj.SubjectUID).Error; err != nil { + } else if err := Db().Model(&entity.Face{}).Where("id = ? AND subj_uid = ''", m.FaceID).Update("SubjUID", subj.SubjUID).Error; err != nil { return affected, err } } @@ -120,21 +120,21 @@ func CreateMarkerSubjects() (affected int64, err error) { return affected, err } -// SearchSubjectUIDs finds subject UIDs matching the search string, and removes names from the remaining query. -func SearchSubjectUIDs(s string) (result []string, names []string, remaining string) { +// SearchSubjUIDs finds subject UIDs matching the search string, and removes names from the remaining query. +func SearchSubjUIDs(s string) (result []string, names []string, remaining string) { if s == "" { return result, names, s } type Matches struct { - SubjectUID string - SubjectName string - SubjectAlias string + SubjUID string + SubjName string + SubjAlias string } var matches []Matches - wheres := LikeAllNames(Cols{"subject_name", "subject_alias"}, s) + wheres := LikeAllNames(Cols{"subj_name", "subj_alias"}, s) if len(wheres) == 0 { return result, names, s @@ -155,16 +155,16 @@ func SearchSubjectUIDs(s string) (result []string, names []string, remaining str } for _, m := range matches { - subj = append(subj, m.SubjectUID) - names = append(names, m.SubjectName) + subj = append(subj, m.SubjUID) + names = append(names, m.SubjName) - for _, r := range txt.Words(strings.ToLower(m.SubjectName)) { + for _, r := range txt.Words(strings.ToLower(m.SubjName)) { if len(r) > 1 { remaining = strings.ReplaceAll(remaining, r, "") } } - for _, r := range txt.Words(strings.ToLower(m.SubjectAlias)) { + for _, r := range txt.Words(strings.ToLower(m.SubjAlias)) { if len(r) > 1 { remaining = strings.ReplaceAll(remaining, r, "") } diff --git a/internal/query/subjects_test.go b/internal/query/subjects_test.go index 1bbc19818..a93df6497 100644 --- a/internal/query/subjects_test.go +++ b/internal/query/subjects_test.go @@ -72,9 +72,9 @@ func TestCreateMarkerSubjects(t *testing.T) { assert.LessOrEqual(t, int64(0), affected) } -func TestSearchSubjectUIDs(t *testing.T) { +func TestSearchSubjUIDs(t *testing.T) { t.Run("john & his | cats", func(t *testing.T) { - result, names, remaining := SearchSubjectUIDs("john & his | cats") + result, names, remaining := SearchSubjUIDs("john & his | cats") if len(result) != 1 { t.Fatal("expected one result") @@ -85,11 +85,11 @@ func TestSearchSubjectUIDs(t *testing.T) { } }) t.Run("xxx", func(t *testing.T) { - result, _, _ := SearchSubjectUIDs("xxx") + result, _, _ := SearchSubjUIDs("xxx") assert.Empty(t, result) }) t.Run("empty string", func(t *testing.T) { - result, _, _ := SearchSubjectUIDs("") + result, _, _ := SearchSubjUIDs("") assert.Empty(t, result) }) } diff --git a/internal/server/routes.go b/internal/server/routes.go index 05084c412..e94dd393f 100644 --- a/internal/server/routes.go +++ b/internal/server/routes.go @@ -103,6 +103,9 @@ func registerRoutes(router *gin.Engine, conf *config.Config) { api.GetSubjects(v1) api.GetSubject(v1) + api.UpdateSubject(v1) + api.LikeSubject(v1) + api.DislikeSubject(v1) api.LabelCover(v1) api.GetLabels(v1)
- Issues labeled help wanted / - easy can be good (first) - contributions. - Our Developer Guide contains all information - necessary to get you started. -
+ Try again using other filters or keywords. +