Backend: Refactor entity package

Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
Michael Mayer 2020-04-30 20:07:03 +02:00
parent 7d840d4a46
commit 260cca91fe
55 changed files with 329 additions and 323 deletions

4
go.mod
View file

@ -1,7 +1,6 @@
module github.com/photoprism/photoprism
require (
github.com/DATA-DOG/go-sqlmock v1.3.3 // indirect
github.com/araddon/dateparse v0.0.0-20190622164848-0fb0a474d195
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd // indirect
github.com/coreos/etcd v3.3.10+incompatible // indirect
@ -55,11 +54,9 @@ require (
github.com/satori/go.uuid v1.2.0
github.com/sevlyar/go-daemon v0.1.5
github.com/shopspring/decimal v0.0.0-20191130220710-360f2bc03045 // indirect
github.com/simplereach/timeutils v1.2.0 // indirect
github.com/sirupsen/logrus v1.5.0
github.com/soheilhy/cmux v0.1.4 // indirect
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 // indirect
github.com/stretchr/objx v0.2.0 // indirect
github.com/stretchr/testify v1.5.1
github.com/studio-b12/gowebdav v0.0.0-20200303150724-9380631c29a1
github.com/tensorflow/tensorflow v1.15.2
@ -80,7 +77,6 @@ require (
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
golang.org/x/tools v0.0.0-20200401192744-099440627f01 // indirect
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce // indirect
gopkg.in/stretchr/testify.v1 v1.2.2 // indirect
gopkg.in/ugjka/go-tz.v2 v2.0.8
gopkg.in/yaml.v2 v2.2.8

40
go.sum
View file

@ -5,8 +5,6 @@ cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU=
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/DATA-DOG/go-sqlmock v1.3.3 h1:CWUqKXe0s8A2z6qCgkP4Kru7wC11YoAnoupUKFDnH08=
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
@ -17,8 +15,6 @@ github.com/apache/thrift v0.0.0-20161221203622-b2a4d4ae21c7 h1:Fv9bK1Q+ly/ROk4aJ
github.com/apache/thrift v0.0.0-20161221203622-b2a4d4ae21c7/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.12.0 h1:pODnxUFNcjP9UTLZGTdeh+j16A8lJbRvD3rOtrk/7bs=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/araddon/dateparse v0.0.0-20181123171228-21df004e09ca h1:7tLEgJZb8/+TI8fLso4lINkuSOI4DqQYwhFB+nRH7RQ=
github.com/araddon/dateparse v0.0.0-20181123171228-21df004e09ca/go.mod h1:SLqhdZcd+dF3TEVL2RMoob5bBP5R1P1qkox+HtCBgGI=
github.com/araddon/dateparse v0.0.0-20190622164848-0fb0a474d195 h1:c4mLfegoDw6OhSJXTd2jUEQgZUQuJWtocudb97Qn9EM=
github.com/araddon/dateparse v0.0.0-20190622164848-0fb0a474d195/go.mod h1:SLqhdZcd+dF3TEVL2RMoob5bBP5R1P1qkox+HtCBgGI=
github.com/beorn7/perks v0.0.0-20160229213445-3ac7bf7a47d1/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
@ -63,32 +59,22 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/djherbis/times v1.1.0 h1:NFhBDODme0XNX+/5ETW9qL6v3Ty57psiXIQBrzzg44E=
github.com/djherbis/times v1.1.0/go.mod h1:CGMZlo255K5r4Yw0b9RRfFQpM2y7uOmxg4jm9HsaVf8=
github.com/djherbis/times v1.2.0 h1:xANXjsC/iBqbO00vkWlYwPWgBgEVU6m6AFYg0Pic+Mc=
github.com/djherbis/times v1.2.0/go.mod h1:CGMZlo255K5r4Yw0b9RRfFQpM2y7uOmxg4jm9HsaVf8=
github.com/dsoprea/go-exif/v2 v2.0.0-20200113231207-0bbb7a3584f7 h1:+koSu4BOaLu+dy50WEj+ltzEjMzK5evzPawKxgIQerw=
github.com/dsoprea/go-exif/v2 v2.0.0-20200113231207-0bbb7a3584f7/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E=
github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4 h1:bVaiYo8amn7Lu93sz6mTlYB3EtLG9aRcMnM1Eps8fmM=
github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E=
github.com/dsoprea/go-jpeg-image-structure v0.0.0-20200113231507-4c1dccae8069 h1:z1wUugJVR5E3jZrXiqzzK4Bgws2YN3R7G0N49AqiykM=
github.com/dsoprea/go-jpeg-image-structure v0.0.0-20200113231507-4c1dccae8069/go.mod h1:J/Nq2z38/2hLpCvcXMVPYI28WN7+sgYpzZ3+jtcj+6U=
github.com/dsoprea/go-jpeg-image-structure v0.0.0-20200401235850-9261959559e1 h1:EQeJZeebsYbccEzUMOpYNXB9AkMZxOmb7x2LgdHhZ3c=
github.com/dsoprea/go-jpeg-image-structure v0.0.0-20200401235850-9261959559e1/go.mod h1:5vwlfhyZI7u8AuvTl0G70sdqVdH41f7dscvBQ6mEbHs=
github.com/dsoprea/go-jpeg-image-structure v0.0.0-20200419165912-75b7a4f392e6 h1:6oyE0L+MX1iUjldwrLdAaU95g36UrKpbmlyslhyoJj4=
github.com/dsoprea/go-jpeg-image-structure v0.0.0-20200419165912-75b7a4f392e6/go.mod h1:5vwlfhyZI7u8AuvTl0G70sdqVdH41f7dscvBQ6mEbHs=
github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696 h1:VGFnZAcLwPpt1sHlAxml+pGLZz9A2s+K/s1YNhPC91Y=
github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696/go.mod h1:Nm/x2ZUNRW6Fe5C3LxdY1PyZY5wmDv/s5dkPJ/VB3iA=
github.com/dsoprea/go-logging v0.0.0-20200401235223-7e979d0e0d02 h1:Ezh2FrKVTFWkL0UVYH7DLx89SvBy9c4SeiG7odG8Yok=
github.com/dsoprea/go-logging v0.0.0-20200401235223-7e979d0e0d02/go.mod h1:Nm/x2ZUNRW6Fe5C3LxdY1PyZY5wmDv/s5dkPJ/VB3iA=
github.com/dsoprea/go-png-image-structure v0.0.0-20200113231611-5ee8d5825a92 h1:D/wlE1zb04BwhLmSARC6kdzXHwrihmSinLn16dIdJt4=
github.com/dsoprea/go-png-image-structure v0.0.0-20200113231611-5ee8d5825a92/go.mod h1:E2uyTIhMXw4weEZrqrz1vFPtXVXrmikgCFRkcDv4nSg=
github.com/dsoprea/go-png-image-structure v0.0.0-20200402000326-c0fdb803026f h1:cuf7rjLGedkB0V4UcWmWMxDZGKNtCM0dGt1WTWsZXDQ=
github.com/dsoprea/go-png-image-structure v0.0.0-20200402000326-c0fdb803026f/go.mod h1:wFgCouxC0gy+d+sre8HkLj+VeUWPr969zAOvQ9NGXJc=
github.com/dsoprea/go-utility v0.0.0-20200322154813-27f0b0d142d7 h1:DJhSHW0odJrW5wR9MU6ry5S+PsxuRXA165KFaiB+cZo=
github.com/dsoprea/go-utility v0.0.0-20200322154813-27f0b0d142d7/go.mod h1:xv8CVgDmI/Shx/X+EUXyXELVnH5lSRUYRija52OHq7E=
github.com/dsoprea/go-utility v0.0.0-20200322184706-df132586647c h1:LMZ1OpXrehc3ES8TAUUJo0UUHR+vZ5AHx5INrhSHdpM=
github.com/dsoprea/go-utility v0.0.0-20200322184706-df132586647c/go.mod h1:95+K3z2L0mqsVYd6yveIv1lmtT3tcQQ3dVakPySffW8=
github.com/dsoprea/go-utility v0.0.0-20200412174200-5aee815e0920 h1:/sPqpPAmg/hrNrlW6EjFrKMQkiJ4888voMOG0uYHNec=
github.com/dsoprea/go-utility v0.0.0-20200412174200-5aee815e0920/go.mod h1:95+K3z2L0mqsVYd6yveIv1lmtT3tcQQ3dVakPySffW8=
github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
@ -174,12 +160,8 @@ github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51
github.com/gorilla/mux v0.0.0-20170228224354-599cba5e7b61/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gosimple/slug v1.5.0 h1:AIIjgCjHcLpX8LzM2NpG4QGW9kUfqv0OLiFRfPv/H3E=
github.com/gosimple/slug v1.5.0/go.mod h1:ER78kgg1Mv0NQGlXiDe57DpCyfbNywXXZ9mIorhxAf0=
github.com/gosimple/slug v1.9.0 h1:r5vDcYrFz9BmfIAMC829un9hq7hKM4cHUrsv36LbEqs=
github.com/gosimple/slug v1.9.0/go.mod h1:AMZ+sOVe65uByN3kgEyf9WEBKBCSS+dJjMX9x4vDJbg=
github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20171020063731-82921fcf811d/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
@ -229,8 +211,6 @@ github.com/lib/pq v1.1.0 h1:/5u4a+KGJptBRqGzPvYQL9p0d/tPR4S31+Tnzj9lEO4=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lucasb-eyer/go-colorful v1.0.2 h1:mCMFu6PgSozg9tDNMMK3g18oJBX7oYGrC09mS6CXfO4=
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/machinebox/progress v0.2.0/go.mod h1:hl4FywxSjfmkmCrersGhmJH7KwuKl+Ueq9BXkOny+iE=
@ -339,13 +319,9 @@ github.com/shopspring/decimal v0.0.0-20191130220710-360f2bc03045 h1:8CnFGhoe92Iz
github.com/shopspring/decimal v0.0.0-20191130220710-360f2bc03045/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/simplereach/timeutils v1.2.0 h1:btgOAlu9RW6de2r2qQiONhjgxdAG7BL6je0G6J/yPnA=
github.com/simplereach/timeutils v1.2.0/go.mod h1:VVbQDfN/FHRZa1LSqcwo4kNZ62OOyqLLGQKYB3pB0Q8=
github.com/sirupsen/logrus v0.0.0-20170323161349-3bcb09397d6d/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.5.0 h1:1N5EYkVAPEywqZRJd7cwnRtCb6xJx7NH3T3WUTF980Q=
github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo=
github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
@ -356,8 +332,6 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
@ -368,10 +342,6 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/studio-b12/gowebdav v0.0.0-20200303150724-9380631c29a1 h1:TPyHV/OgChqNcnYqCoCvIFjR9TU60gFXXBKnhOBzVEI=
github.com/studio-b12/gowebdav v0.0.0-20200303150724-9380631c29a1/go.mod h1:gCcfDlA1Y7GqOaeEKw5l9dOGx1VLdc/HuQSlQAaZ30s=
github.com/tensorflow/tensorflow v1.14.0 h1:g0W2+f/RybcvmrTjPLTwXkfr/BsDGUd8FKT6ZzojOMo=
github.com/tensorflow/tensorflow v1.14.0/go.mod h1:itOSERT4trABok4UOoG+X4BoKds9F3rIsySdn+Lvu90=
github.com/tensorflow/tensorflow v1.15.0 h1:ujwCjW4LRUwUp7YrAK+menE0GZkeJgkQT5FgdWNpVCk=
github.com/tensorflow/tensorflow v1.15.0/go.mod h1:itOSERT4trABok4UOoG+X4BoKds9F3rIsySdn+Lvu90=
github.com/tensorflow/tensorflow v1.15.2 h1:7/f/A664Tml/nRJg04+p3StcrsT53mkcvmxYHXI21Qo=
github.com/tensorflow/tensorflow v1.15.2/go.mod h1:itOSERT4trABok4UOoG+X4BoKds9F3rIsySdn+Lvu90=
github.com/tidwall/gjson v1.6.0 h1:9VEQWz6LLMUsUl6PueE49ir4Ka6CzLymOAZDxpFsTDc=
@ -427,8 +397,6 @@ golang.org/x/crypto v0.0.0-20180503215945-1f94bef427e3/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4 h1:ydJNl0ENAG67pFbB+9tfhiL2pYqLhfoaZFw/cjLhY4A=
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@ -456,8 +424,6 @@ golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8ou
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd h1:QPwSajcTUrFriMF1nJ3XzgoqakqQEsnZf9LdXdi2nkI=
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -490,8 +456,6 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -502,8 +466,6 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138 h1:H3uGjxCR/6Ds0Mjgyp7LMK81+LvmbvWWEnJhzk1Pi9E=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c h1:KfpJVdWhuRqNk4XVXzjXf2KAV4TBEP77SYdFGjeGuIE=
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200401192744-099440627f01 h1:ysQJ/fU6laLOZJseIeOqXl6Mo+lw5z6b7QHnmUKjW+k=
golang.org/x/tools v0.0.0-20200401192744-099440627f01/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
@ -533,8 +495,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce h1:xcEWjVhvbDy+nHP67nPDDpbYrY+ILlfndk4bRioVHaU=
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/stretchr/testify.v1 v1.2.2 h1:yhQC6Uy5CqibAIlk1wlusa/MJ3iAN49/BsR/dCCKz3M=

View file

@ -10,7 +10,7 @@ import (
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/internal/workers"
"github.com/photoprism/photoprism/pkg/txt"
)
@ -25,7 +25,7 @@ func GetAccounts(router *gin.RouterGroup, conf *config.Config) {
var f form.AccountSearch
q := query.New(conf.Db())
q := service.Query()
err := c.MustBindWith(&f, binding.Form)
if err != nil {
@ -59,7 +59,7 @@ func GetAccount(router *gin.RouterGroup, conf *config.Config) {
return
}
q := query.New(conf.Db())
q := service.Query()
id := ParseUint(c.Param("id"))
if m, err := q.AccountByID(id); err == nil {
@ -81,7 +81,7 @@ func GetAccountDirs(router *gin.RouterGroup, conf *config.Config) {
return
}
q := query.New(conf.Db())
q := service.Query()
id := ParseUint(c.Param("id"))
m, err := q.AccountByID(id)
@ -114,7 +114,7 @@ func ShareWithAccount(router *gin.RouterGroup, conf *config.Config) {
return
}
q := query.New(conf.Db())
q := service.Query()
id := ParseUint(c.Param("id"))
m, err := q.AccountByID(id)
@ -143,7 +143,7 @@ func ShareWithAccount(router *gin.RouterGroup, conf *config.Config) {
dstFileName := dst + "/" + file.ShareFileName()
fileShare := entity.NewFileShare(file.ID, m.ID, dstFileName)
fileShare.FirstOrCreate(conf.Db())
fileShare.FirstOrCreate()
}
workers.StartShare(conf)
@ -173,7 +173,7 @@ func CreateAccount(router *gin.RouterGroup, conf *config.Config) {
return
}
m, err := entity.CreateAccount(f, conf.Db())
m, err := entity.CreateAccount(f)
log.Debugf("create account: %+v %+v", f, m)
@ -202,7 +202,7 @@ func UpdateAccount(router *gin.RouterGroup, conf *config.Config) {
id := ParseUint(c.Param("id"))
q := query.New(conf.Db())
q := service.Query()
m, err := q.AccountByID(id)
@ -228,7 +228,7 @@ func UpdateAccount(router *gin.RouterGroup, conf *config.Config) {
}
// 3) Save model with values from form
if err := m.Save(f, conf.Db()); err != nil {
if err := m.Save(f); err != nil {
log.Error(err)
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrSaveFailed)
return
@ -259,7 +259,7 @@ func DeleteAccount(router *gin.RouterGroup, conf *config.Config) {
}
id := ParseUint(c.Param("id"))
q := query.New(conf.Db())
q := service.Query()
m, err := q.AccountByID(id)
@ -268,7 +268,7 @@ func DeleteAccount(router *gin.RouterGroup, conf *config.Config) {
return
}
if err := m.Delete(conf.Db()); err != nil {
if err := m.Delete(); err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, txt.UcFirst(err.Error()))
return
}

View file

@ -14,7 +14,7 @@ import (
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/internal/thumb"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/rnd"
@ -35,7 +35,7 @@ func GetAlbums(router *gin.RouterGroup, conf *config.Config) {
var f form.AlbumSearch
q := query.New(conf.Db())
q := service.Query()
err := c.MustBindWith(&f, binding.Form)
if err != nil {
@ -61,7 +61,7 @@ func GetAlbums(router *gin.RouterGroup, conf *config.Config) {
func GetAlbum(router *gin.RouterGroup, conf *config.Config) {
router.GET("/albums/:uuid", func(c *gin.Context) {
id := c.Param("uuid")
q := query.New(conf.Db())
q := service.Query()
m, err := q.AlbumByUUID(id)
if err != nil {
@ -88,7 +88,7 @@ func CreateAlbum(router *gin.RouterGroup, conf *config.Config) {
return
}
q := query.New(conf.Db())
q := service.Query()
m := entity.NewAlbum(f.AlbumName)
m.AlbumFavorite = f.AlbumFavorite
@ -118,9 +118,8 @@ func UpdateAlbum(router *gin.RouterGroup, conf *config.Config) {
return
}
db := conf.Db()
uuid := c.Param("uuid")
q := query.New(db)
q := service.Query()
m, err := q.AlbumByUUID(uuid)
@ -143,7 +142,7 @@ func UpdateAlbum(router *gin.RouterGroup, conf *config.Config) {
return
}
if err := m.Save(f, conf.Db()); err != nil {
if err := m.Save(f); err != nil {
log.Error(err)
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrSaveFailed)
return
@ -167,7 +166,7 @@ func DeleteAlbum(router *gin.RouterGroup, conf *config.Config) {
}
id := c.Param("uuid")
q := query.New(conf.Db())
q := service.Query()
m, err := q.AlbumByUUID(id)
@ -199,7 +198,7 @@ func LikeAlbum(router *gin.RouterGroup, conf *config.Config) {
}
id := c.Param("uuid")
q := query.New(conf.Db())
q := service.Query()
album, err := q.AlbumByUUID(id)
@ -230,7 +229,7 @@ func DislikeAlbum(router *gin.RouterGroup, conf *config.Config) {
}
id := c.Param("uuid")
q := query.New(conf.Db())
q := service.Query()
album, err := q.AlbumByUUID(id)
if err != nil {
@ -264,7 +263,7 @@ func AddPhotosToAlbum(router *gin.RouterGroup, conf *config.Config) {
}
uuid := c.Param("uuid")
q := query.New(conf.Db())
q := service.Query()
a, err := q.AlbumByUUID(uuid)
if err != nil {
@ -280,11 +279,10 @@ func AddPhotosToAlbum(router *gin.RouterGroup, conf *config.Config) {
return
}
db := conf.Db()
var added []*entity.PhotoAlbum
for _, p := range photos {
added = append(added, entity.NewPhotoAlbum(p.PhotoUUID, a.AlbumUUID).FirstOrCreate(db))
added = append(added, entity.NewPhotoAlbum(p.PhotoUUID, a.AlbumUUID).FirstOrCreate())
}
if len(added) == 1 {
@ -320,7 +318,7 @@ func RemovePhotosFromAlbum(router *gin.RouterGroup, conf *config.Config) {
return
}
q := query.New(conf.Db())
q := service.Query()
a, err := q.AlbumByUUID(c.Param("uuid"))
if err != nil {
@ -345,7 +343,7 @@ func DownloadAlbum(router *gin.RouterGroup, conf *config.Config) {
router.GET("/albums/:uuid/download", func(c *gin.Context) {
start := time.Now()
q := query.New(conf.Db())
q := service.Query()
a, err := q.AlbumByUUID(c.Param("uuid"))
if err != nil {
@ -446,7 +444,7 @@ func AlbumThumbnail(router *gin.RouterGroup, conf *config.Config) {
return
}
q := query.New(conf.Db())
q := service.Query()
gc := conf.Cache()
cacheKey := fmt.Sprintf("album-thumbnail:%s:%s", uuid, typeName)

View file

@ -7,11 +7,13 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/service"
)
// NewApiTest returns new API test helper
func NewApiTest() (app *gin.Engine, router *gin.RouterGroup, conf *config.Config) {
conf = config.TestConfig()
service.SetConfig(conf)
gin.SetMode(gin.TestMode)
app = gin.New()
router = app.Group("/api/v1")

View file

@ -5,7 +5,7 @@ import (
"path"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/gin-gonic/gin"
@ -23,7 +23,7 @@ func GetDownload(router *gin.RouterGroup, conf *config.Config) {
router.GET("/download/:hash", func(c *gin.Context) {
fileHash := c.Param("hash")
q := query.New(conf.Db())
q := service.Query()
f, err := q.FileByHash(fileHash)
if err != nil {

View file

@ -7,6 +7,7 @@ import (
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/pkg/txt"
)
@ -21,7 +22,7 @@ func GetFile(router *gin.RouterGroup, conf *config.Config) {
return
}
q := query.New(conf.Db())
q := service.Query()
p, err := q.FileByHash(c.Param("hash"))
if err != nil {

View file

@ -4,7 +4,7 @@ import (
"net/http"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/gin-gonic/gin"
@ -24,7 +24,7 @@ func GetGeo(router *gin.RouterGroup, conf *config.Config) {
var f form.GeoSearch
q := query.New(conf.Db())
q := service.Query()
err := c.MustBindWith(&f, binding.Form)
if err != nil {

View file

@ -13,7 +13,7 @@ import (
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/internal/thumb"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
@ -29,7 +29,7 @@ func GetLabels(router *gin.RouterGroup, conf *config.Config) {
var f form.LabelSearch
q := query.New(conf.Db())
q := service.Query()
err := c.MustBindWith(&f, binding.Form)
if err != nil {
@ -67,7 +67,7 @@ func UpdateLabel(router *gin.RouterGroup, conf *config.Config) {
}
id := c.Param("uuid")
q := query.New(conf.Db())
q := service.Query()
m, err := q.LabelByUUID(id)
@ -99,7 +99,7 @@ func LikeLabel(router *gin.RouterGroup, conf *config.Config) {
}
id := c.Param("uuid")
q := query.New(conf.Db())
q := service.Query()
label, err := q.LabelByUUID(id)
@ -135,7 +135,7 @@ func DislikeLabel(router *gin.RouterGroup, conf *config.Config) {
}
id := c.Param("uuid")
q := query.New(conf.Db())
q := service.Query()
label, err := q.LabelByUUID(id)
@ -180,7 +180,7 @@ func LabelThumbnail(router *gin.RouterGroup, conf *config.Config) {
return
}
q := query.New(conf.Db())
q := service.Query()
gc := conf.Cache()
cacheKey := fmt.Sprintf("label-thumbnail:%s:%s", labelUUID, typeName)

View file

@ -4,7 +4,7 @@ import (
"net/http"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/gin-gonic/gin"
@ -18,7 +18,7 @@ func GetMomentsTime(router *gin.RouterGroup, conf *config.Config) {
return
}
q := query.New(conf.Db())
q := service.Query()
result, err := q.GetMomentsTime()
if err != nil {

View file

@ -11,6 +11,7 @@ import (
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/pkg/fs"
)
@ -25,7 +26,7 @@ func GetPhoto(router *gin.RouterGroup, conf *config.Config) {
return
}
q := query.New(conf.Db())
q := service.Query()
p, err := q.PreloadPhotoByUUID(c.Param("uuid"))
if err != nil {
@ -74,7 +75,7 @@ func UpdatePhoto(router *gin.RouterGroup, conf *config.Config) {
}
// 3) Save model with values from form
if err := entity.SavePhotoForm(m, f, db, conf.GeoCodingApi()); err != nil {
if err := entity.SavePhotoForm(m, f, conf.GeoCodingApi()); err != nil {
log.Error(err)
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrSaveFailed)
return
@ -101,7 +102,7 @@ func UpdatePhoto(router *gin.RouterGroup, conf *config.Config) {
// uuid: string PhotoUUID as returned by the API
func GetPhotoDownload(router *gin.RouterGroup, conf *config.Config) {
router.GET("/photos/:uuid/download", func(c *gin.Context) {
q := query.New(conf.Db())
q := service.Query()
f, err := q.FileByPhotoUUID(c.Param("uuid"))
if err != nil {
@ -141,7 +142,7 @@ func LikePhoto(router *gin.RouterGroup, conf *config.Config) {
}
id := c.Param("uuid")
q := query.New(conf.Db())
q := service.Query()
m, err := q.PhotoByUUID(id)
if err != nil {
@ -175,7 +176,7 @@ func DislikePhoto(router *gin.RouterGroup, conf *config.Config) {
}
id := c.Param("uuid")
q := query.New(conf.Db())
q := service.Query()
m, err := q.PhotoByUUID(id)
if err != nil {

View file

@ -10,6 +10,7 @@ import (
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/pkg/txt"
)
@ -24,7 +25,7 @@ func AddPhotoLabel(router *gin.RouterGroup, conf *config.Config) {
return
}
q := query.New(conf.Db())
q := service.Query()
m, err := q.PhotoByUUID(c.Param("uuid"))
db := conf.Db()
@ -40,7 +41,7 @@ func AddPhotoLabel(router *gin.RouterGroup, conf *config.Config) {
return
}
lm := entity.NewLabel(f.LabelName, f.LabelPriority).FirstOrCreate(db)
lm := entity.NewLabel(f.LabelName, f.LabelPriority).FirstOrCreate()
if lm.New && f.LabelPriority >= 0 {
event.Publish("count.labels", event.Data{
@ -48,7 +49,7 @@ func AddPhotoLabel(router *gin.RouterGroup, conf *config.Config) {
})
}
plm := entity.NewPhotoLabel(m.ID, lm.ID, f.Uncertainty, "manual").FirstOrCreate(db)
plm := entity.NewPhotoLabel(m.ID, lm.ID, f.Uncertainty, "manual").FirstOrCreate()
if plm.Uncertainty > f.Uncertainty {
plm.Uncertainty = f.Uncertainty
@ -68,7 +69,7 @@ func AddPhotoLabel(router *gin.RouterGroup, conf *config.Config) {
return
}
if err := p.Save(db); err != nil {
if err := p.Save(); err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UcFirst(err.Error())})
return
}
@ -130,7 +131,7 @@ func RemovePhotoLabel(router *gin.RouterGroup, conf *config.Config) {
return
}
if err := p.Save(db); err != nil {
if err := p.Save(); err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UcFirst(err.Error())})
return
}
@ -185,7 +186,7 @@ func UpdatePhotoLabel(router *gin.RouterGroup, conf *config.Config) {
return
}
if err := label.Save(db); err != nil {
if err := label.Save(); err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UcFirst(err.Error())})
return
}
@ -197,7 +198,7 @@ func UpdatePhotoLabel(router *gin.RouterGroup, conf *config.Config) {
return
}
if err := p.Save(db); err != nil {
if err := p.Save(); err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UcFirst(err.Error())})
return
}

View file

@ -5,7 +5,7 @@ import (
"strconv"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/gin-gonic/gin"
@ -36,7 +36,7 @@ func GetPhotos(router *gin.RouterGroup, conf *config.Config) {
var f form.PhotoSearch
q := query.New(conf.Db())
q := service.Query()
err := c.MustBindWith(&f, binding.Form)
if err != nil {

View file

@ -13,7 +13,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/internal/thumb"
"github.com/photoprism/photoprism/pkg/fs"
)
@ -45,7 +45,7 @@ func GetPreview(router *gin.RouterGroup, conf *config.Config) {
f.Count = 12
f.Order = "relevance"
q := query.New(conf.Db())
q := service.Query()
p, _, err := q.Photos(f)
if err != nil {

View file

@ -12,7 +12,7 @@ import (
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/rnd"
"github.com/photoprism/photoprism/pkg/txt"
@ -47,7 +47,7 @@ func CreateZip(router *gin.RouterGroup, conf *config.Config) {
return
}
q := query.New(conf.Db())
q := service.Query()
files, err := q.FilesByUUID(f.Photos, 1000, 0)
if err != nil {

View file

@ -43,7 +43,7 @@ func copyAction(ctx *cli.Context) error {
return err
}
conf.MigrateDb()
conf.InitDb()
// get cli first argument
sourcePath := strings.TrimSpace(ctx.Args().First())

View file

@ -43,7 +43,7 @@ func importAction(ctx *cli.Context) error {
return err
}
conf.MigrateDb()
conf.InitDb()
// get cli first argument
sourcePath := strings.TrimSpace(ctx.Args().First())

View file

@ -42,7 +42,7 @@ func indexAction(ctx *cli.Context) error {
return err
}
conf.MigrateDb()
conf.InitDb()
log.Infof("indexing photos in %s", conf.OriginalsPath())
if conf.ReadOnly() {

View file

@ -28,7 +28,7 @@ func migrateAction(ctx *cli.Context) error {
log.Infoln("migrating database")
conf.MigrateDb()
conf.InitDb()
elapsed := time.Since(start)

View file

@ -80,7 +80,7 @@ func startAction(ctx *cli.Context) error {
}
// initialize the database
conf.MigrateDb()
conf.InitDb()
// check if daemon is running, if not initialize the daemon
dctx := new(daemon.Context)

View file

@ -36,7 +36,7 @@ func (c *Config) DatabaseDsn() string {
// Db returns the db connection.
func (c *Config) Db() *gorm.DB {
if c.db == nil {
log.Fatal("config: database not initialised")
log.Fatal("config: database not connected")
}
return c.db
@ -55,16 +55,15 @@ func (c *Config) CloseDb() error {
return nil
}
// MigrateDb will start a migration process.
func (c *Config) MigrateDb() {
db := c.Db()
entity.Migrate(db)
// InitDb will initialize the database connection and schema.
func (c *Config) InitDb() {
entity.SetDbProvider(c)
entity.Migrate()
}
// DropTables drops all tables in the currently configured database (be careful!).
func (c *Config) DropTables() {
db := c.Db()
entity.DropTables(db)
entity.DropTables(c.Db())
}
// connectToDatabase establishes a database connection.
@ -99,11 +98,11 @@ func (c *Config) connectToDatabase(ctx context.Context) error {
log.Infof("starting database server at %s:%d\n", c.TidbServerHost(), c.TidbServerPort())
go tidb.Start(ctx, c.TidbServerPath(), c.TidbServerPort(), c.TidbServerHost(), c.Debug())
time.Sleep(2 * time.Second)
}
for i := 1; i <= 12; i++ {
time.Sleep(5 * time.Second)
db, err = gorm.Open(dbDriver, dbDsn)
if db != nil && err == nil {
@ -119,6 +118,8 @@ func (c *Config) connectToDatabase(ctx context.Context) error {
initSuccess = true
}
}
time.Sleep(5 * time.Second)
}
if err != nil || db == nil {

View file

@ -108,12 +108,12 @@ func NewTestConfig() *Config {
// Make sure changes have been written to disk.
time.Sleep(250 * time.Millisecond)
c.MigrateDb()
c.InitDb()
// Make sure changes have been written to disk.
time.Sleep(250 * time.Millisecond)
entity.CreateTestFixtures(c.Db())
entity.CreateTestFixtures()
// TODO: Remove when new test fixtures are ready
c.ImportSQL(c.ExamplesPath() + "/fixtures.sql")
@ -134,7 +134,7 @@ func NewTestErrorConfig() *Config {
log.Fatalf("config: %s", err.Error())
}
c.MigrateDb()
c.InitDb()
return c
}

View file

@ -5,7 +5,6 @@ import (
"sort"
"time"
"github.com/jinzhu/gorm"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/remote"
"github.com/photoprism/photoprism/internal/remote/webdav"
@ -52,7 +51,7 @@ type Account struct {
}
// CreateAccount creates a new account entity in the database.
func CreateAccount(form form.Account, db *gorm.DB) (model *Account, err error) {
func CreateAccount(form form.Account) (model *Account, err error) {
model = &Account{
ShareSize: "",
ShareExpires: 0,
@ -60,13 +59,15 @@ func CreateAccount(form form.Account, db *gorm.DB) (model *Account, err error) {
SyncStatus: AccountSyncStatusRefresh,
}
err = model.Save(form, db)
err = model.Save(form)
return model, err
}
// Save updates the entity using form data and stores it in the database.
func (m *Account) Save(form form.Account, db *gorm.DB) error {
func (m *Account) Save(form form.Account) error {
db := Db()
if err := deepcopier.Copy(m).From(form); err != nil {
return err
}
@ -95,8 +96,8 @@ func (m *Account) Save(form form.Account, db *gorm.DB) error {
}
// Delete deletes the entity from the database.
func (m *Account) Delete(db *gorm.DB) error {
return db.Delete(m).Error
func (m *Account) Delete() error {
return Db().Delete(m).Error
}
// Directories returns a list of directories or albums in an account.

View file

@ -73,7 +73,7 @@ func (m *Album) SetName(name string) {
}
// Save updates the entity using form data and stores it in the database.
func (m *Album) Save(f form.Album, db *gorm.DB) error {
func (m *Album) Save(f form.Album) error {
if err := deepcopier.Copy(m).From(f); err != nil {
return err
}
@ -82,5 +82,5 @@ func (m *Album) Save(f form.Album, db *gorm.DB) error {
m.SetName(f.AlbumName)
}
return db.Save(m).Error
return Db().Save(m).Error
}

View file

@ -6,8 +6,6 @@ import (
"time"
"github.com/gosimple/slug"
"github.com/jinzhu/gorm"
"github.com/photoprism/photoprism/internal/mutex"
"github.com/photoprism/photoprism/pkg/txt"
)
@ -32,8 +30,8 @@ var UnknownCamera = Camera{
}
// CreateUnknownCamera initializes the database with an unknown camera if not exists
func CreateUnknownCamera(db *gorm.DB) {
UnknownCamera.FirstOrCreate(db)
func CreateUnknownCamera() {
UnknownCamera.FirstOrCreate()
}
// NewCamera creates a camera entity from a model name and a make name.
@ -64,10 +62,9 @@ func NewCamera(modelName string, makeName string) *Camera {
return result
}
// FirstOrCreate checks wether the camera model exist already in the database
func (m *Camera) FirstOrCreate(db *gorm.DB) *Camera {
mutex.Db.Lock()
defer mutex.Db.Unlock()
// FirstOrCreate checks if the camera model exist already in the database
func (m *Camera) FirstOrCreate() *Camera {
db := Db()
if err := db.FirstOrCreate(m, "camera_model = ? AND camera_make = ?", m.CameraModel, m.CameraMake).Error; err != nil {
log.Errorf("camera: %s", err)

View file

@ -6,6 +6,14 @@ import (
"github.com/stretchr/testify/assert"
)
func TestCamera_FirstOrCreate(t *testing.T) {
t.Run("iphone-se", func(t *testing.T) {
camera := NewCamera("iPhone SE", "Apple")
camera.FirstOrCreate()
assert.GreaterOrEqual(t, camera.ID, uint(1))
})
}
func TestNewCamera(t *testing.T) {
t.Run("unknown camera", func(t *testing.T) {
camera := NewCamera("", "Nikon")

View file

@ -4,7 +4,6 @@ import (
"github.com/gosimple/slug"
"github.com/jinzhu/gorm"
"github.com/photoprism/photoprism/internal/maps"
"github.com/photoprism/photoprism/internal/mutex"
)
// altCountryNames defines mapping between different names for the same countriy
@ -34,8 +33,8 @@ var UnknownCountry = Country{
}
// CreateUnknownCountry is used to initialize the database with the default country
func CreateUnknownCountry(db *gorm.DB) {
UnknownCountry.FirstOrCreate(db)
func CreateUnknownCountry() {
UnknownCountry.FirstOrCreate()
}
// NewCountry creates a new country, with default country code if not provided
@ -59,12 +58,9 @@ func NewCountry(countryCode string, countryName string) *Country {
return result
}
// FirstOrCreate checks wether the country exist already in the database (using countryCode)
func (m *Country) FirstOrCreate(db *gorm.DB) *Country {
mutex.Db.Lock()
defer mutex.Db.Unlock()
if err := db.FirstOrCreate(m, "id = ?", m.ID).Error; err != nil {
// FirstOrCreate checks if the country exist already in the database (using countryCode)
func (m *Country) FirstOrCreate() *Country {
if err := Db().FirstOrCreate(m, "id = ?", m.ID).Error; err != nil {
log.Errorf("country: %s", err)
}

89
internal/entity/db.go Normal file
View file

@ -0,0 +1,89 @@
package entity
import (
"fmt"
"sync"
"time"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
)
var dbProvider DbProvider
type DbProvider interface {
Db() *gorm.DB
}
// SetDbProvider sets the provider to get a gorm db connection.
func SetDbProvider(provider DbProvider) {
dbProvider = provider
}
// Db() returns a database connection.
func Db() *gorm.DB {
return dbProvider.Db()
}
// Db() returns an unscoped database connection.
func Unscoped() *gorm.DB {
return Db().Unscoped()
}
type Gorm struct {
Driver string
Dsn string
once sync.Once
db *gorm.DB
}
// Db returns the gorm db connection.
func (g *Gorm) Db() *gorm.DB {
g.once.Do(g.Connect)
if g.db == nil {
log.Fatal("entity: database not connected")
}
return g.db
}
// Connect creates a new gorm db connection.
func (g *Gorm) Connect() {
db, err := gorm.Open(g.Driver, g.Dsn)
if err != nil || db == nil {
for i := 1; i <= 12; i++ {
fmt.Printf("gorm.Open(%s, %s) %d\n", g.Driver, g.Dsn, i)
db, err = gorm.Open(g.Driver, g.Dsn)
if db != nil && err == nil {
break
} else {
time.Sleep(5 * time.Second)
}
}
if err != nil || db == nil {
fmt.Println(err)
log.Fatal(err)
}
}
db.LogMode(false)
db.SetLogger(log)
g.db = db
}
// Close closes the gorm db connection.
func (g *Gorm) Close() {
if g.db != nil {
if err := g.db.Close(); err != nil {
log.Fatal(err)
}
g.db = nil
}
}

View file

@ -1,10 +1,5 @@
package entity
import (
"github.com/jinzhu/gorm"
"github.com/photoprism/photoprism/internal/mutex"
)
// Description stores additional metadata fields for each photo to improve search performance.
type Description struct {
PhotoID uint `gorm:"primary_key;auto_increment:false"`
@ -18,11 +13,8 @@ type Description struct {
}
// FirstOrCreate returns the matching entity or creates a new one.
func (m *Description) FirstOrCreate(db *gorm.DB) error {
mutex.Db.Lock()
defer mutex.Db.Unlock()
return db.FirstOrCreate(m, "photo_id = ?", m.PhotoID).Error
func (m *Description) FirstOrCreate() error {
return Db().FirstOrCreate(m, "photo_id = ?", m.PhotoID).Error
}
// NoDescription checks if the photo has no Description

View file

@ -23,8 +23,8 @@ func logError(result *gorm.DB) {
}
// Migrate creates all tables and inserts default entities as needed.
func Migrate(db *gorm.DB) {
db.AutoMigrate(
func Migrate() {
Db().AutoMigrate(
&Account{},
&File{},
&FileShare{},
@ -46,10 +46,10 @@ func Migrate(db *gorm.DB) {
&Link{},
)
CreateUnknownPlace(db)
CreateUnknownCountry(db)
CreateUnknownCamera(db)
CreateUnknownLens(db)
CreateUnknownPlace()
CreateUnknownCountry()
CreateUnknownCamera()
CreateUnknownLens()
}
// DropTables drops database tables for all known entities.

View file

@ -14,6 +14,18 @@ func TestMain(m *testing.M) {
log = logrus.StandardLogger()
log.Out = &logBuffer
log.SetLevel(logrus.DebugLevel)
db := &Gorm{
Driver: "mysql",
Dsn: "photoprism:photoprism@tcp(photoprism-db:4001)/photoprism?parseTime=true",
}
SetDbProvider(db)
Migrate()
code := m.Run()
db.Close()
os.Exit(code)
}

View file

@ -52,10 +52,10 @@ type File struct {
}
// FirstFileByHash gets a file in db from its hash
func FirstFileByHash(db *gorm.DB, fileHash string) (File, error) {
func FirstFileByHash(fileHash string) (File, error) {
var file File
q := db.Unscoped().First(&file, "file_hash = ?", fileHash)
q := Db().Unscoped().First(&file, "file_hash = ?", fileHash)
return file, q.Error
}

View file

@ -2,9 +2,6 @@ package entity
import (
"time"
"github.com/jinzhu/gorm"
"github.com/photoprism/photoprism/internal/mutex"
)
const (
@ -48,11 +45,8 @@ func NewFileShare(fileID, accountID uint, remoteName string) *FileShare {
}
// FirstOrCreate returns the matching entity or creates a new one.
func (m *FileShare) FirstOrCreate(db *gorm.DB) *FileShare {
mutex.Db.Lock()
defer mutex.Db.Unlock()
if err := db.FirstOrCreate(m, "file_id = ? AND account_id = ? AND remote_name = ?", m.FileID, m.AccountID, m.RemoteName).Error; err != nil {
func (m *FileShare) FirstOrCreate() *FileShare {
if err := Db().FirstOrCreate(m, "file_id = ? AND account_id = ? AND remote_name = ?", m.FileID, m.AccountID, m.RemoteName).Error; err != nil {
log.Errorf("file push: %s", err)
}

View file

@ -2,9 +2,6 @@ package entity
import (
"time"
"github.com/jinzhu/gorm"
"github.com/photoprism/photoprism/internal/mutex"
)
const (
@ -48,11 +45,8 @@ func NewFileSync(accountID uint, remoteName string) *FileSync {
}
// FirstOrCreate returns the matching entity or creates a new one.
func (m *FileSync) FirstOrCreate(db *gorm.DB) *FileSync {
mutex.Db.Lock()
defer mutex.Db.Unlock()
if err := db.FirstOrCreate(m, "account_id = ? AND remote_name = ?", m.AccountID, m.RemoteName).Error; err != nil {
func (m *FileSync) FirstOrCreate() *FileSync {
if err := Db().FirstOrCreate(m, "account_id = ? AND remote_name = ?", m.AccountID, m.RemoteName).Error; err != nil {
log.Errorf("file sync: %s", err)
}

View file

@ -1,10 +1,6 @@
package entity
import (
"github.com/jinzhu/gorm"
)
// CreateTestFixtures inserts all known entities into the database for testing.
func CreateTestFixtures(db *gorm.DB) {
CreateLabelFixtures(db)
func CreateTestFixtures() {
CreateLabelFixtures()
}

View file

@ -3,8 +3,6 @@ package entity
import (
"strings"
"github.com/jinzhu/gorm"
"github.com/photoprism/photoprism/internal/mutex"
"github.com/photoprism/photoprism/pkg/txt"
)
@ -26,12 +24,9 @@ func NewKeyword(keyword string) *Keyword {
return result
}
// FirstOrCreate checks wether the keyword already exist in the database
func (m *Keyword) FirstOrCreate(db *gorm.DB) *Keyword {
mutex.Db.Lock()
defer mutex.Db.Unlock()
if err := db.FirstOrCreate(m, "keyword = ?", m.Keyword).Error; err != nil {
// FirstOrCreate checks if the keyword already exist in the database
func (m *Keyword) FirstOrCreate() *Keyword {
if err := Db().FirstOrCreate(m, "keyword = ?", m.Keyword).Error; err != nil {
log.Errorf("keyword: %s", err)
}

View file

@ -6,7 +6,6 @@ import (
"github.com/gosimple/slug"
"github.com/jinzhu/gorm"
"github.com/photoprism/photoprism/internal/classify"
"github.com/photoprism/photoprism/internal/mutex"
"github.com/photoprism/photoprism/pkg/rnd"
"github.com/photoprism/photoprism/pkg/txt"
)
@ -62,11 +61,8 @@ func NewLabel(name string, priority int) *Label {
}
// FirstOrCreate checks if the label already exists in the database
func (m *Label) FirstOrCreate(db *gorm.DB) *Label {
mutex.Db.Lock()
defer mutex.Db.Unlock()
if err := db.FirstOrCreate(m, "label_slug = ? OR custom_slug = ?", m.LabelSlug, m.CustomSlug).Error; err != nil {
func (m *Label) FirstOrCreate() *Label {
if err := Db().FirstOrCreate(m, "label_slug = ? OR custom_slug = ?", m.LabelSlug, m.CustomSlug).Error; err != nil {
log.Errorf("label: %s", err)
}
@ -91,8 +87,9 @@ func (m *Label) SetName(name string) {
}
// Updates a label if necessary
func (m *Label) Update(label classify.Label, db *gorm.DB) error {
func (m *Label) Update(label classify.Label) error {
save := false
db := Db()
if m.LabelPriority != label.Priority {
m.LabelPriority = label.Priority
@ -120,7 +117,7 @@ func (m *Label) Update(label classify.Label, db *gorm.DB) error {
// Add categories
for _, category := range label.Categories {
sn := NewLabel(txt.Title(category), -3).FirstOrCreate(db)
sn := NewLabel(txt.Title(category), -3).FirstOrCreate()
if err := db.Model(m).Association("LabelCategories").Append(sn).Error; err != nil {
return err
}

View file

@ -2,8 +2,6 @@ package entity
import (
"time"
"github.com/jinzhu/gorm"
)
var LabelFixtures = map[string]Label{
@ -27,8 +25,8 @@ var LabelFixtures = map[string]Label{
}
// CreateLabelFixtures inserts known entities into the database for testing.
func CreateLabelFixtures(db *gorm.DB) {
func CreateLabelFixtures() {
for _, entity := range LabelFixtures {
db.Create(&entity)
Db().Create(&entity)
}
}

View file

@ -5,8 +5,6 @@ import (
"time"
"github.com/gosimple/slug"
"github.com/jinzhu/gorm"
"github.com/photoprism/photoprism/internal/mutex"
)
// Lens represents camera lens (as extracted from UpdateExif metadata)
@ -31,8 +29,8 @@ var UnknownLens = Lens{
}
// CreateUnknownLens initializes the database with an unknown lens if not exists
func CreateUnknownLens(db *gorm.DB) {
UnknownLens.FirstOrCreate(db)
func CreateUnknownLens() {
UnknownLens.FirstOrCreate()
}
// TableName returns Lens table identifier "lens"
@ -60,11 +58,8 @@ func NewLens(modelName string, makeName string) *Lens {
}
// FirstOrCreate checks if the lens already exists in the database
func (m *Lens) FirstOrCreate(db *gorm.DB) *Lens {
mutex.Db.Lock()
defer mutex.Db.Unlock()
if err := db.FirstOrCreate(m, "lens_slug = ?", m.LensSlug).Error; err != nil {
func (m *Lens) FirstOrCreate() *Lens {
if err := Db().FirstOrCreate(m, "lens_slug = ?", m.LensSlug).Error; err != nil {
log.Errorf("lens: %s", err)
}

View file

@ -4,7 +4,6 @@ import (
"strings"
"time"
"github.com/jinzhu/gorm"
"github.com/photoprism/photoprism/internal/maps"
"github.com/photoprism/photoprism/pkg/s2"
"github.com/photoprism/photoprism/pkg/txt"
@ -32,7 +31,9 @@ func NewLocation(lat, lng float32) *Location {
}
// Find gets the location using either the db or the api if not in the db
func (m *Location) Find(db *gorm.DB, api string) error {
func (m *Location) Find(api string) error {
db := Db()
if err := db.Preload("Place").First(m, "id = ?", m.ID).Error; err == nil {
return nil
}
@ -45,7 +46,7 @@ func (m *Location) Find(db *gorm.DB, api string) error {
return err
}
if place := FindPlaceByLabel(l.S2Token(), l.Label(), db); place != nil {
if place := FindPlaceByLabel(l.S2Token(), l.Label()); place != nil {
m.Place = place
} else {
m.Place = &Place{

View file

@ -67,7 +67,8 @@ type Photo struct {
}
// SavePhotoForm updates a model using form data and persists it in the database.
func SavePhotoForm(model Photo, form form.Photo, db *gorm.DB, geoApi string) error {
func SavePhotoForm(model Photo, form form.Photo, geoApi string) error {
db := Db()
locChanged := model.PhotoLat != form.PhotoLat || model.PhotoLng != form.PhotoLng
if err := deepcopier.Copy(&model).From(form); err != nil {
@ -83,9 +84,9 @@ func SavePhotoForm(model Photo, form form.Photo, db *gorm.DB, geoApi string) err
}
if model.HasLatLng() && locChanged && model.LocationSrc == SrcManual {
locKeywords, labels := model.UpdateLocation(db, geoApi)
locKeywords, labels := model.UpdateLocation(geoApi)
model.AddLabels(labels, db)
model.AddLabels(labels)
w := txt.UniqueKeywords(model.Description.PhotoKeywords)
w = append(w, locKeywords...)
@ -97,7 +98,7 @@ func SavePhotoForm(model Photo, form form.Photo, db *gorm.DB, geoApi string) err
log.Warnf("%s (%s)", err.Error(), model.PhotoUUID)
}
if err := model.IndexKeywords(db); err != nil {
if err := model.IndexKeywords(); err != nil {
log.Warnf("%s (%s)", err.Error(), model.PhotoUUID)
}
@ -109,7 +110,8 @@ func SavePhotoForm(model Photo, form form.Photo, db *gorm.DB, geoApi string) err
}
// Save stored the entity in the database.
func (m *Photo) Save(db *gorm.DB) error {
func (m *Photo) Save() error {
db := Db()
labels := m.ClassifyLabels()
if err := m.UpdateTitle(labels); err != nil {
@ -122,7 +124,7 @@ func (m *Photo) Save(db *gorm.DB) error {
m.Description.PhotoKeywords = strings.Join(txt.UniqueWords(w), ", ")
}
if err := m.IndexKeywords(db); err != nil {
if err := m.IndexKeywords(); err != nil {
log.Error(err)
}
@ -181,11 +183,13 @@ func (m *Photo) BeforeSave(scope *gorm.Scope) error {
}
// IndexKeywords adds given keywords to the photo entry
func (m *Photo) IndexKeywords(db *gorm.DB) error {
func (m *Photo) IndexKeywords() error {
if !m.DescriptionLoaded() {
return fmt.Errorf("photo: can't index keywords, description not loaded (%s)", m.PhotoUUID)
}
db := Db()
var keywordIds []uint
var keywords []string
@ -199,7 +203,7 @@ func (m *Photo) IndexKeywords(db *gorm.DB) error {
keywords = txt.UniqueWords(keywords)
for _, w := range keywords {
kw := NewKeyword(w).FirstOrCreate(db)
kw := NewKeyword(w).FirstOrCreate()
if kw.Skip {
continue
@ -207,15 +211,15 @@ func (m *Photo) IndexKeywords(db *gorm.DB) error {
keywordIds = append(keywordIds, kw.ID)
NewPhotoKeyword(m.ID, kw.ID).FirstOrCreate(db)
NewPhotoKeyword(m.ID, kw.ID).FirstOrCreate()
}
return db.Where("photo_id = ? AND keyword_id NOT IN (?)", m.ID, keywordIds).Delete(&PhotoKeyword{}).Error
}
// PreloadFiles prepares gorm scope to retrieve photo file
func (m *Photo) PreloadFiles(db *gorm.DB) {
q := db.NewScope(nil).DB().
func (m *Photo) PreloadFiles() {
q := Db().NewScope(nil).DB().
Table("files").
Select(`files.*`).
Where("files.photo_id = ?", m.ID).
@ -224,8 +228,8 @@ func (m *Photo) PreloadFiles(db *gorm.DB) {
logError(q.Scan(&m.Files))
}
/* func (m *Photo) PreloadLabels(db *gorm.DB) {
q := db.NewScope(nil).DB().
/* func (m *Photo) PreloadLabels() {
q := Db().NewScope(nil).DB().
Table("labels").
Select(`labels.*`).
Joins("JOIN photos_labels ON photos_labels.label_id = labels.id AND photos_labels.photo_id = ?", m.ID).
@ -236,8 +240,8 @@ func (m *Photo) PreloadFiles(db *gorm.DB) {
} */
// PreloadKeywords prepares gorm scope to retrieve photo keywords
func (m *Photo) PreloadKeywords(db *gorm.DB) {
q := db.NewScope(nil).DB().
func (m *Photo) PreloadKeywords() {
q := Db().NewScope(nil).DB().
Table("keywords").
Select(`keywords.*`).
Joins("JOIN photos_keywords ON photos_keywords.keyword_id = keywords.id AND photos_keywords.photo_id = ?", m.ID).
@ -247,8 +251,8 @@ func (m *Photo) PreloadKeywords(db *gorm.DB) {
}
// PreloadAlbums prepares gorm scope to retrieve photo albums
func (m *Photo) PreloadAlbums(db *gorm.DB) {
q := db.NewScope(nil).DB().
func (m *Photo) PreloadAlbums() {
q := Db().NewScope(nil).DB().
Table("albums").
Select(`albums.*`).
Joins("JOIN photos_albums ON photos_albums.album_uuid = albums.album_uuid AND photos_albums.photo_uuid = ?", m.PhotoUUID).
@ -259,11 +263,11 @@ func (m *Photo) PreloadAlbums(db *gorm.DB) {
}
// PreloadMany prepares gorm scope to retrieve photo file, albums and keywords
func (m *Photo) PreloadMany(db *gorm.DB) {
m.PreloadFiles(db)
// m.PreloadLabels(db)
m.PreloadKeywords(db)
m.PreloadAlbums(db)
func (m *Photo) PreloadMany() {
m.PreloadFiles()
// m.PreloadLabels()
m.PreloadKeywords()
m.PreloadAlbums()
}
// NoLocation checks if the photo has no location
@ -369,10 +373,12 @@ func (m *Photo) UpdateTitle(labels classify.Labels) error {
}
// AddLabels updates the entity with additional or updated label information.
func (m *Photo) AddLabels(labels classify.Labels, db *gorm.DB) {
func (m *Photo) AddLabels(labels classify.Labels) {
db := Db()
// TODO: Update classify labels from database
for _, label := range labels {
lm := NewLabel(label.Title(), label.Priority).FirstOrCreate(db)
lm := NewLabel(label.Title(), label.Priority).FirstOrCreate()
if lm.New {
event.EntitiesCreated("labels", []*Label{lm})
@ -384,11 +390,11 @@ func (m *Photo) AddLabels(labels classify.Labels, db *gorm.DB) {
}
}
if err := lm.Update(label, db); err != nil {
if err := lm.Update(label); err != nil {
log.Errorf("index: %s", err)
}
plm := NewPhotoLabel(m.ID, lm.ID, label.Uncertainty, label.Source).FirstOrCreate(db)
plm := NewPhotoLabel(m.ID, lm.ID, label.Uncertainty, label.Source).FirstOrCreate()
if plm.Uncertainty > label.Uncertainty && plm.Uncertainty > 100 {
plm.Uncertainty = label.Uncertainty

View file

@ -2,9 +2,6 @@ package entity
import (
"time"
"github.com/jinzhu/gorm"
"github.com/photoprism/photoprism/internal/mutex"
)
// PhotoAlbum represents the many_to_many relation between Photo and Album
@ -33,12 +30,9 @@ func NewPhotoAlbum(photoUUID, albumUUID string) *PhotoAlbum {
return result
}
// FirstOrCreate checks wether the PhotoAlbum relation already exist in the database before the creation
func (m *PhotoAlbum) FirstOrCreate(db *gorm.DB) *PhotoAlbum {
mutex.Db.Lock()
defer mutex.Db.Unlock()
if err := db.FirstOrCreate(m, "photo_uuid = ? AND album_uuid = ?", m.PhotoUUID, m.AlbumUUID).Error; err != nil {
// FirstOrCreate checks if the PhotoAlbum relation already exist in the database before the creation
func (m *PhotoAlbum) FirstOrCreate() *PhotoAlbum {
if err := Db().FirstOrCreate(m, "photo_uuid = ? AND album_uuid = ?", m.PhotoUUID, m.AlbumUUID).Error; err != nil {
log.Errorf("photo album: %s", err)
}

View file

@ -1,10 +1,5 @@
package entity
import (
"github.com/jinzhu/gorm"
"github.com/photoprism/photoprism/internal/mutex"
)
// PhotoKeyword represents the many-to-many relation between Photo and Keyword
type PhotoKeyword struct {
PhotoID uint `gorm:"primary_key;auto_increment:false"`
@ -26,12 +21,9 @@ func NewPhotoKeyword(photoID, keywordID uint) *PhotoKeyword {
return result
}
// FirstOrCreate checks wether the PhotoKeywords relation already exist in the database before the creation
func (m *PhotoKeyword) FirstOrCreate(db *gorm.DB) *PhotoKeyword {
mutex.Db.Lock()
defer mutex.Db.Unlock()
if err := db.FirstOrCreate(m, "photo_id = ? AND keyword_id = ?", m.PhotoID, m.KeywordID).Error; err != nil {
// FirstOrCreate checks if the PhotoKeywords relation already exist in the database before the creation
func (m *PhotoKeyword) FirstOrCreate() *PhotoKeyword {
if err := Db().FirstOrCreate(m, "photo_id = ? AND keyword_id = ?", m.PhotoID, m.KeywordID).Error; err != nil {
log.Errorf("photo keyword: %s", err)
}

View file

@ -1,9 +1,7 @@
package entity
import (
"github.com/jinzhu/gorm"
"github.com/photoprism/photoprism/internal/classify"
"github.com/photoprism/photoprism/internal/mutex"
)
// PhotoLabel represents the many-to-many relation between Photo and label.
@ -35,11 +33,8 @@ func NewPhotoLabel(photoID, labelID uint, uncertainty int, source string) *Photo
}
// FirstOrCreate checks if the PhotoLabel relation already exist in the database before the creation
func (m *PhotoLabel) FirstOrCreate(db *gorm.DB) *PhotoLabel {
mutex.Db.Lock()
defer mutex.Db.Unlock()
if err := db.FirstOrCreate(m, "photo_id = ? AND label_id = ?", m.PhotoID, m.LabelID).Error; err != nil {
func (m *PhotoLabel) FirstOrCreate() *PhotoLabel {
if err := Db().FirstOrCreate(m, "photo_id = ? AND label_id = ?", m.PhotoID, m.LabelID).Error; err != nil {
log.Errorf("photo label: %s", err)
}
@ -63,7 +58,7 @@ func (m *PhotoLabel) ClassifyLabel() classify.Label {
}
// Save saves the entity in the database and returns an error.
func (m *PhotoLabel) Save(db *gorm.DB) error {
func (m *PhotoLabel) Save() error {
if m.Photo != nil {
m.Photo = nil
}
@ -72,5 +67,5 @@ func (m *PhotoLabel) Save(db *gorm.DB) error {
m.Label.SetName(m.Label.LabelName)
}
return db.Save(m).Error
return Db().Save(m).Error
}

View file

@ -3,7 +3,6 @@ package entity
import (
"time"
"github.com/jinzhu/gorm"
"github.com/photoprism/photoprism/internal/classify"
"github.com/photoprism/photoprism/internal/event"
"gopkg.in/ugjka/go-tz.v2/tz"
@ -43,10 +42,10 @@ func (m *Photo) GetTakenAt() time.Time {
}
// UpdateLocation updates location and labels based on latitude and longitude.
func (m *Photo) UpdateLocation(db *gorm.DB, geoApi string) (keywords []string, labels classify.Labels) {
func (m *Photo) UpdateLocation(geoApi string) (keywords []string, labels classify.Labels) {
var location = NewLocation(m.PhotoLat, m.PhotoLng)
err := location.Find(db, geoApi)
err := location.Find(geoApi)
if err == nil {
if location.Place.New {
@ -66,7 +65,7 @@ func (m *Photo) UpdateLocation(db *gorm.DB, geoApi string) (keywords []string, l
m.TakenAt = m.GetTakenAt()
}
country := NewCountry(location.CountryCode(), location.CountryName()).FirstOrCreate(db)
country := NewCountry(location.CountryCode(), location.CountryName()).FirstOrCreate()
if country.New {
event.Publish("count.countries", event.Data{

View file

@ -5,7 +5,6 @@ import (
"github.com/jinzhu/gorm"
"github.com/photoprism/photoprism/internal/maps"
"github.com/photoprism/photoprism/internal/mutex"
)
// Place used to associate photos to places
@ -36,8 +35,8 @@ var UnknownPlace = Place{
}
// CreateUnknownPlace initializes default place in the database
func CreateUnknownPlace(db *gorm.DB) {
UnknownPlace.FirstOrCreate(db)
func CreateUnknownPlace() {
UnknownPlace.FirstOrCreate()
}
// AfterCreate sets the New column used for database callback
@ -46,10 +45,10 @@ func (m *Place) AfterCreate(scope *gorm.Scope) error {
}
// FindPlaceByLabel returns a place from an id or a label
func FindPlaceByLabel(id string, label string, db *gorm.DB) *Place {
func FindPlaceByLabel(id string, label string) *Place {
place := &Place{}
if err := db.First(place, "id = ? OR loc_label = ?", id, label).Error; err != nil {
if err := Db().First(place, "id = ? OR loc_label = ?", id, label).Error; err != nil {
log.Debugf("place: %s for id %s or label \"%s\"", err.Error(), id, label)
return nil
}
@ -58,20 +57,17 @@ func FindPlaceByLabel(id string, label string, db *gorm.DB) *Place {
}
// Find returns db record of place
func (m *Place) Find(db *gorm.DB) error {
if err := db.First(m, "id = ?", m.ID).Error; err != nil {
func (m *Place) Find() error {
if err := Db().First(m, "id = ?", m.ID).Error; err != nil {
return err
}
return nil
}
// FirstOrCreate checks wether the place already exists in the database
func (m *Place) FirstOrCreate(db *gorm.DB) *Place {
mutex.Db.Lock()
defer mutex.Db.Unlock()
if err := db.FirstOrCreate(m, "id = ? OR loc_label = ?", m.ID, m.LocLabel).Error; err != nil {
// FirstOrCreate checks if the place already exists in the database
func (m *Place) FirstOrCreate() *Place {
if err := Db().FirstOrCreate(m, "id = ? OR loc_label = ?", m.ID, m.LocLabel).Error; err != nil {
log.Debugf("place: %s for token %s or label \"%s\"", err.Error(), m.ID, m.LocLabel)
}

View file

@ -193,7 +193,7 @@ func (imp *Import) DestinationFilename(mainFile *MediaFile, mediaFile *MediaFile
dateCreated := mainFile.DateCreated()
if !mediaFile.IsSidecar() {
if f, err := entity.FirstFileByHash(imp.conf.Db(), mediaFile.Hash()); err == nil {
if f, err := entity.FirstFileByHash(mediaFile.Hash()); err == nil {
existingFilename := imp.conf.OriginalsPath() + string(os.PathSeparator) + f.FileName
return existingFilename, fmt.Errorf("\"%s\" is identical to \"%s\" (%s)", mediaFile.FileName(), f.FileName, mediaFile.Hash())
}

View file

@ -193,8 +193,8 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
if photo.CameraSrc == entity.SrcAuto && (fileChanged || o.UpdateCamera) {
// Set UpdateCamera, Lens, Focal Length and F Number
photo.Camera = entity.NewCamera(m.CameraModel(), m.CameraMake()).FirstOrCreate(ind.db)
photo.Lens = entity.NewLens(m.LensModel(), m.LensMake()).FirstOrCreate(ind.db)
photo.Camera = entity.NewCamera(m.CameraModel(), m.CameraMake()).FirstOrCreate()
photo.Lens = entity.NewLens(m.LensModel(), m.LensMake()).FirstOrCreate()
photo.PhotoFocalLength = m.FocalLength()
photo.PhotoFNumber = m.FNumber()
photo.PhotoIso = m.Iso()
@ -208,7 +208,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
if fileChanged || o.UpdateKeywords || o.UpdateLocation || o.UpdateTitle || photo.NoTitle() {
if photo.HasLatLng() {
var locLabels classify.Labels
locKeywords, locLabels = photo.UpdateLocation(ind.db, ind.conf.GeoCodingApi())
locKeywords, locLabels = photo.UpdateLocation(ind.conf.GeoCodingApi())
labels = append(labels, locLabels...)
} else {
log.Info("index: no latitude and longitude in metadata")
@ -326,7 +326,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
event.EntitiesCreated("photos", []entity.Photo{photo})
}
photo.AddLabels(labels, ind.db)
photo.AddLabels(labels)
file.PhotoID = photo.ID
result.PhotoID = photo.ID
@ -370,7 +370,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
return result
}
if err := photo.IndexKeywords(ind.db); err != nil {
if err := photo.IndexKeywords(); err != nil {
log.Warnf("%s (%s)", err.Error(), photo.PhotoUUID)
}
} else {

View file

@ -24,7 +24,7 @@ func TestMediaFile_Location(t *testing.T) {
t.Fatal(err)
}
if err = location.Find(conf.Db(), "places"); err != nil {
if err = location.Find("places"); err != nil {
t.Fatal(err)
}
@ -39,7 +39,7 @@ func TestMediaFile_Location(t *testing.T) {
t.Fatal(err)
}
if err = location.Find(conf.Db(), "places"); err != nil {
if err = location.Find("places"); err != nil {
t.Fatal(err)
}
@ -60,7 +60,7 @@ func TestMediaFile_Location(t *testing.T) {
t.Fatal(err)
}
if err = location.Find(conf.Db(), "places"); err != nil {
if err = location.Find("places"); err != nil {
t.Fatal(err)
}

View file

@ -1,19 +0,0 @@
package query
import (
"testing"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/entity"
"github.com/stretchr/testify/assert"
)
// Example for using database fixtures defined in assets/resources/examples/fixtures.sql
func TestCamera_FirstOrCreate(t *testing.T) {
t.Run("iphone-se", func(t *testing.T) {
camera := entity.NewCamera("iPhone SE", "Apple")
c := config.TestConfig()
camera.FirstOrCreate(c.Db())
assert.GreaterOrEqual(t, camera.ID, uint(1))
})
}

View file

@ -3,7 +3,6 @@ package query
import (
"testing"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/entity"
"github.com/stretchr/testify/assert"
@ -26,8 +25,7 @@ func TestNewCountry(t *testing.T) {
func TestCountry_FirstOrCreate(t *testing.T) {
t.Run("country already existing", func(t *testing.T) {
country := entity.NewCountry("de", "Germany")
c := config.TestConfig()
country.FirstOrCreate(c.Db())
country.FirstOrCreate()
assert.Equal(t, "de", country.Code())
assert.Equal(t, "Germany", country.Name())
assert.Equal(t, "Country Description", country.CountryDescription)
@ -36,8 +34,7 @@ func TestCountry_FirstOrCreate(t *testing.T) {
})
t.Run("country not yet existing", func(t *testing.T) {
country := entity.NewCountry("wl", "Wonder Land")
c := config.TestConfig()
country.FirstOrCreate(c.Db())
country.FirstOrCreate()
assert.Equal(t, "wl", country.Code())
assert.Equal(t, "Wonder Land", country.Name())
})

View file

@ -452,7 +452,7 @@ func (q *Query) PreloadPhotoByUUID(photoUUID string) (photo entity.Photo, err er
return photo, err
}
photo.PreloadMany(q.db)
photo.PreloadMany()
return photo, nil
}

19
internal/service/query.go Normal file
View file

@ -0,0 +1,19 @@
package service
import (
"sync"
"github.com/photoprism/photoprism/internal/query"
)
var onceQuery sync.Once
func initQuery() {
services.Query = query.New(Config().Db())
}
func Query() *query.Query {
onceQuery.Do(initQuery)
return services.Query
}

View file

@ -5,6 +5,7 @@ import (
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/nsfw"
"github.com/photoprism/photoprism/internal/photoprism"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/session"
)
@ -18,6 +19,7 @@ var services struct {
Resample *photoprism.Resample
Classify *classify.TensorFlow
Session *session.Session
Query *query.Query
}
func SetConfig(c *config.Config) {

View file

@ -62,7 +62,7 @@ func (s *Sync) refresh(a entity.Account) (complete bool, err error) {
}
}
f.FirstOrCreate(db)
f.FirstOrCreate()
if f.Status == entity.FileSyncIgnore && mediaType == fs.MediaRaw && a.SyncRaw {
f.Status = entity.FileSyncNew