Some more comment improvements (#257)

* Improve comment in classify package

* improve comment in config package

* improve entity package comments

* grammar error in comments
This commit is contained in:
François d'Yvoire 2020-02-21 00:14:45 +00:00 committed by GitHub
parent 4fe5aaaccd
commit 55693fab35
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 161 additions and 27 deletions

View file

@ -1,5 +1,5 @@
/*
This package encapsulates image classification using TensorFlow
Package classify encapsulates image classification functionnality using TensorFlow
Additional information can be found in our Developer Guide:

View file

@ -15,6 +15,7 @@ import (
"gopkg.in/yaml.v2"
)
// LabelRule defines the rule for a given Label
type LabelRule struct {
Label string
See string
@ -25,6 +26,7 @@ type LabelRule struct {
type LabelRules map[string]LabelRule
// This function generates the rules.go file containing rule extracted from rules.yml file
func main() {
rules := make(LabelRules)

View file

@ -1,5 +1,6 @@
package classify
// LabelRule defines the rule for a given Label
type LabelRule struct {
Label string
Threshold float32
@ -7,8 +8,10 @@ type LabelRule struct {
Priority int
}
// LabelRules is a map of rules with label name as index
type LabelRules map[string]LabelRule
// Find is a getter for LabelRules that give a default rule with a non-zero threshold for missing keys
func (rules LabelRules) Find(label string) LabelRule {
if rule, ok := rules[label]; ok {
return rule

View file

@ -9,6 +9,7 @@ import (
// Labels is list of MediaFile labels.
type Labels []Label
// Implements functions for the Sort Interface. Default Labels sort is by priority and uncertainty
func (l Labels) Len() int { return len(l) }
func (l Labels) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l Labels) Less(i, j int) bool {
@ -19,6 +20,7 @@ func (l Labels) Less(i, j int) bool {
}
}
// AppendLabel extends append func by not appending empty label
func (l Labels) AppendLabel(label Label) Labels {
if label.Name == "" {
return l
@ -27,6 +29,7 @@ func (l Labels) AppendLabel(label Label) Labels {
return append(l, label)
}
// Keywords returns all keywords contains in Labels and theire categories
func (l Labels) Keywords() (result []string) {
for _, label := range l {
result = append(result, txt.Keywords(label.Name)...)
@ -39,9 +42,11 @@ func (l Labels) Keywords() (result []string) {
return result
}
// Title gets the best label out a list of labels or fallback to compute a meaningfull default title.
func (l Labels) Title(fallback string) string {
fallbackRunes := len([]rune(fallback))
// check if given fallback is valid
if fallbackRunes < 2 || fallbackRunes > 25 || txt.ContainsNumber(fallback) {
fallback = ""
}

View file

@ -18,7 +18,7 @@ import (
tf "github.com/tensorflow/tensorflow/tensorflow/go"
)
// TensorFlow if a wrapper for their low-level API.
// TensorFlow is a wrapper for tensorflow low-level API.
type TensorFlow struct {
conf *config.Config
model *tf.SavedModel
@ -34,6 +34,7 @@ func New(modelsPath string, disabled bool) *TensorFlow {
return &TensorFlow{modelsPath: modelsPath, disabled: disabled, modelName: "nasnet", modelTags: []string{"photoprism"}}
}
// Init initialises tensorflow models if not disabled
func (t *TensorFlow) Init() (err error) {
if t.disabled {
return nil
@ -154,15 +155,17 @@ func (t *TensorFlow) loadModel() error {
return t.loadLabels(modelPath)
}
// bestLabels returns the best 5 labels (if enough high probability labels) from the prediction of the model
func (t *TensorFlow) bestLabels(probabilities []float32) Labels {
// Make a list of label/probability pairs
var result Labels
for i, p := range probabilities {
if i >= len(t.labels) {
// break if probabilities and labels does not match
break
}
// discard labels with low probabilities
if p < 0.1 {
continue
}
@ -171,10 +174,12 @@ func (t *TensorFlow) bestLabels(probabilities []float32) Labels {
rule := rules.Find(labelText)
// discard labels that don't met the threshold
if p < rule.Threshold {
continue
}
// Get rule label name instead of t.labels name if it exists
if rule.Label != "" {
labelText = rule.Label
}
@ -189,6 +194,7 @@ func (t *TensorFlow) bestLabels(probabilities []float32) Labels {
// Sort by probability
sort.Sort(result)
// return only the 5 best labels
if l := len(result); l < 5 {
return result[:l]
} else {
@ -196,6 +202,7 @@ func (t *TensorFlow) bestLabels(probabilities []float32) Labels {
}
}
// makeTensor converts bytes jpeg image in a tensor object required as tensorflow model input
func (t *TensorFlow) makeTensor(image []byte, imageFormat string) (*tf.Tensor, error) {
img, err := imaging.Decode(bytes.NewReader(image), imaging.AutoOrientation(true))

View file

@ -14,6 +14,7 @@ func isBcrypt(s string) bool {
return b
}
// CheckPassword compares given password p with the admin password
func (c *Config) CheckPassword(p string) bool {
ap := c.AdminPassword()

View file

@ -9,7 +9,7 @@ import (
"github.com/photoprism/photoprism/pkg/fs"
)
// HTTP client / Web UI config values
// ClientConfig contains HTTP client / Web UI config values
type ClientConfig map[string]interface{}
// PublicClientConfig returns reduced config values for non-public sites.

View file

@ -20,6 +20,7 @@ import (
var log = event.Log
// Config holds database, cache and all parameters of photoprism
type Config struct {
db *gorm.DB
cache *gc.Cache
@ -27,6 +28,7 @@ type Config struct {
}
func init() {
// initialize the Thumbnails global variable
for name, t := range thumb.Types {
if t.Public {
thumbnail := Thumbnail{Name: name, Width: t.Width, Height: t.Height}
@ -52,6 +54,7 @@ func initLogger(debug bool) {
})
}
// NewConfig initialises a new configuration file
func NewConfig(ctx *cli.Context) *Config {
initLogger(ctx.GlobalBool("debug"))
@ -107,7 +110,7 @@ func (c *Config) Author() string {
return c.config.Author
}
// Description returns the twitter handle for sharing.
// Twitter returns the twitter handle for sharing.
func (c *Config) Twitter() string {
return c.config.Twitter
}

View file

@ -4,6 +4,7 @@ import (
"errors"
)
// Define photoprism specific errors
var (
ErrReadOnly = errors.New("not available in read-only mode")
ErrUnauthorized = errors.New("please log in and try again")

View file

@ -4,7 +4,7 @@ import (
"github.com/urfave/cli"
)
// Global CLI flags
// GlobalFlags lists all CLI flags
var GlobalFlags = []cli.Flag{
cli.BoolFlag{
Name: "debug",

View file

@ -13,6 +13,7 @@ import (
"gopkg.in/yaml.v2"
)
// define database drivers const
const (
DbTiDB = "internal"
DbMySQL = "mysql"
@ -78,7 +79,7 @@ type Params struct {
ThumbFilter string `yaml:"thumb-filter" flag:"thumb-filter"`
}
// NewParams() creates a new configuration entity by using two methods:
// NewParams creates a new configuration entity by using two methods:
//
// 1. SetValuesFromFile: This will initialize values from a yaml config file.
//
@ -103,6 +104,7 @@ func NewParams(ctx *cli.Context) *Params {
return c
}
// expandFilenames converts path in config to absolute path
func (c *Params) expandFilenames() {
c.ConfigPath = fs.Abs(c.ConfigPath)
c.ResourcesPath = fs.Abs(c.ResourcesPath)

View file

@ -9,11 +9,13 @@ import (
"gopkg.in/yaml.v2"
)
// Settings contains Web UI settings
type Settings struct {
Theme string `json:"theme" yaml:"theme" flag:"theme"`
Language string `json:"language" yaml:"language" flag:"language"`
}
// NewSettings returns a empty Settings
func NewSettings() *Settings {
return &Settings{}
}

View file

@ -16,6 +16,7 @@ import (
"github.com/urfave/cli"
)
// define constants used for testing the config package
const (
TestDataZip = "/tmp/photoprism/testdata.zip"
TestDataURL = "https://dl.photoprism.org/fixtures/testdata.zip"
@ -29,6 +30,7 @@ func testDataPath(assetsPath string) string {
return assetsPath + "/testdata"
}
// NewTestParams inits valid params used for testing
func NewTestParams() *Params {
assetsPath := fs.Abs("../../assets")
@ -52,6 +54,7 @@ func NewTestParams() *Params {
return c
}
// NewTestParamsError inits invalid params used for testing
func NewTestParamsError() *Params {
assetsPath := fs.Abs("../..")
@ -71,6 +74,7 @@ func NewTestParamsError() *Params {
return c
}
// TestConfig inits the global testConfig if it was not already initialised
func TestConfig() *Config {
once.Do(func() {
testConfig = NewTestConfig()
@ -79,6 +83,7 @@ func TestConfig() *Config {
return testConfig
}
// NewTestConfig inits valid config used for testing
func NewTestConfig() *Config {
log.SetLevel(logrus.DebugLevel)
@ -102,6 +107,7 @@ func NewTestConfig() *Config {
return c
}
// NewTestErrorConfig inits invalid config used for testing
func NewTestErrorConfig() *Config {
log.SetLevel(logrus.DebugLevel)
@ -115,7 +121,7 @@ func NewTestErrorConfig() *Config {
return c
}
// Returns example cli config for testing
// CliTestContext returns example cli config for testing
func CliTestContext() *cli.Context {
config := NewTestParams()
@ -146,6 +152,7 @@ func CliTestContext() *cli.Context {
return c
}
// RemoveTestData deletes files in import, export, originals and cache folders
func (c *Config) RemoveTestData(t *testing.T) {
os.RemoveAll(c.ImportPath())
os.RemoveAll(c.ExportPath())
@ -153,6 +160,7 @@ func (c *Config) RemoveTestData(t *testing.T) {
os.RemoveAll(c.CachePath())
}
// DownloadTestData downloads test data from photoprism.org server
func (c *Config) DownloadTestData(t *testing.T) {
if fs.FileExists(TestDataZip) {
hash := fs.Hash(TestDataZip)
@ -172,12 +180,14 @@ func (c *Config) DownloadTestData(t *testing.T) {
}
}
// UnzipTestData in default test folder
func (c *Config) UnzipTestData(t *testing.T) {
if _, err := fs.Unzip(TestDataZip, testDataPath(c.AssetsPath())); err != nil {
t.Logf("could not unzip test data: %s\n", err.Error())
}
}
// InitializeTestData using testing constant
func (c *Config) InitializeTestData(t *testing.T) {
t.Log("initializing test data")

View file

@ -1,9 +1,11 @@
package config
// Thumbnail gives direct access to width and height for a thumbnail setting
type Thumbnail struct {
Name string
Width int
Height int
}
// Thumbnails is a list of default thumbnail size available for the app
var Thumbnails []Thumbnail

View file

@ -10,7 +10,7 @@ import (
"github.com/photoprism/photoprism/pkg/rnd"
)
// Photo album
// Album represents a photo album
type Album struct {
ID uint `gorm:"primary_key"`
CoverUUID string `gorm:"type:varbinary(36);"`
@ -29,6 +29,7 @@ type Album struct {
DeletedAt *time.Time `sql:"index"`
}
// BeforeCreate computes a random UUID when a new album is created in database
func (m *Album) BeforeCreate(scope *gorm.Scope) error {
if err := scope.SetColumn("AlbumUUID", rnd.PPID('a')); err != nil {
return err
@ -37,6 +38,7 @@ func (m *Album) BeforeCreate(scope *gorm.Scope) error {
return nil
}
// NewAlbum creates a new album; default name is current month and year
func NewAlbum(albumName string) *Album {
albumName = strings.TrimSpace(albumName)
@ -54,6 +56,7 @@ func NewAlbum(albumName string) *Album {
return result
}
// Rename an existing album
func (m *Album) Rename(albumName string) {
if albumName == "" {
albumName = m.CreatedAt.Format("January 2006")

View file

@ -24,6 +24,7 @@ type Camera struct {
DeletedAt *time.Time `sql:"index"`
}
// NewCamera creates a camera entity from a model name and a make name.
func NewCamera(modelName string, makeName string) *Camera {
makeName = strings.TrimSpace(makeName)
@ -50,6 +51,7 @@ 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()
@ -61,6 +63,7 @@ func (m *Camera) FirstOrCreate(db *gorm.DB) *Camera {
return m
}
// String returns a string designing the given Camera entity
func (m *Camera) String() string {
if m.CameraMake != "" && m.CameraModel != "" {
return fmt.Sprintf("%s %s", m.CameraMake, m.CameraModel)

View file

@ -1,6 +1,6 @@
package entity
// Labels can have zero or more categories with the same or a similar meaning
// Category of labels regroups labels with the same or a similar meaning using a main/root label
type Category struct {
LabelID uint `gorm:"primary_key;auto_increment:false"`
CategoryID uint `gorm:"primary_key;auto_increment:false"`
@ -8,6 +8,7 @@ type Category struct {
Category *Label
}
// TableName returns Category table identifier "categories"
func (Category) TableName() string {
return "categories"
}

View file

@ -7,12 +7,14 @@ import (
"github.com/photoprism/photoprism/internal/mutex"
)
// altCountryNames defines mapping between different names for the same countriy
var altCountryNames = map[string]string{
"United States of America": "USA",
"United States": "USA",
"": "Unknown",
}
// Country represents a country location, used for labeling photos.
type Country struct {
ID string `gorm:"primary_key"`
CountrySlug string `gorm:"type:varbinary(128);unique_index;"`
@ -24,13 +26,15 @@ type Country struct {
New bool `gorm:"-"`
}
// UnknownCountry is the default country
var UnknownCountry = NewCountry("zz", maps.CountryNames["zz"])
// CreateUnknownCountry is used to initialize the database with the default country
func CreateUnknownCountry(db *gorm.DB) {
UnknownCountry.FirstOrCreate(db)
}
// Create a new country
// NewCountry creates a new country, with default country code if not provided
func NewCountry(countryCode string, countryName string) *Country {
if countryCode == "" {
countryCode = "zz"
@ -51,6 +55,7 @@ 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()
@ -62,14 +67,17 @@ func (m *Country) FirstOrCreate(db *gorm.DB) *Country {
return m
}
// AfterCreate sets the New column used for database callback
func (m *Country) AfterCreate(scope *gorm.Scope) error {
return scope.SetColumn("New", true)
}
// Code returns country code
func (m *Country) Code() string {
return m.ID
}
// Name returns country name
func (m *Country) Name() string {
return m.CountryName
}

View file

@ -1,5 +1,5 @@
/*
This package contains models for data storage based on GORM.
Package entity contains models for data storage based on GORM.
See http://gorm.io/docs/ for more information about GORM.

View file

@ -7,7 +7,7 @@ import (
"github.com/photoprism/photoprism/pkg/rnd"
)
// Events
// Event defines temporal event that can be used to link photos together
type Event struct {
EventUUID string `gorm:"type:varbinary(36);unique_index;"`
EventSlug string `gorm:"type:varbinary(128);unique_index;"`
@ -25,10 +25,12 @@ type Event struct {
DeletedAt *time.Time `sql:"index"`
}
// TableName returns Event table identifier "events"
func (Event) TableName() string {
return "events"
}
// BeforeCreate computes a random UUID when a new event is created in database
func (e *Event) BeforeCreate(scope *gorm.Scope) error {
return scope.SetColumn("EventUUID", rnd.PPID('e'))
}

View file

@ -10,7 +10,7 @@ import (
"github.com/photoprism/photoprism/pkg/rnd"
)
// An image or sidecar file that belongs to a photo
// File represents an image or sidecar file that belongs to a photo
type File struct {
ID uint `gorm:"primary_key"`
Photo *Photo
@ -47,6 +47,7 @@ type File struct {
DeletedAt *time.Time `sql:"index"`
}
// FirstFileByHash gets a file in db from its hash
func FirstFileByHash(db *gorm.DB, fileHash string) (File, error) {
var file File
@ -55,10 +56,12 @@ func FirstFileByHash(db *gorm.DB, fileHash string) (File, error) {
return file, q.Error
}
// BeforeCreate computes a random UUID when a new file is created in database
func (m *File) BeforeCreate(scope *gorm.Scope) error {
return scope.SetColumn("FileUUID", rnd.PPID('f'))
}
// DownloadFileName returns a name useful for download links
func (m *File) DownloadFileName() string {
if m.Photo == nil {
return fmt.Sprintf("%s.%s", m.FileHash, m.FileType)
@ -79,6 +82,7 @@ func (m *File) DownloadFileName() string {
return result
}
// Changed checks wether given filesize or modified time are matching the current File
func (m File) Changed(fileSize int64, fileModified time.Time) bool {
if m.FileSize != fileSize {
return true

View file

@ -7,13 +7,14 @@ import (
"github.com/photoprism/photoprism/internal/mutex"
)
// Keyword for full text search
// Keyword used for full text search
type Keyword struct {
ID uint `gorm:"primary_key"`
Keyword string `gorm:"type:varchar(64);index;"`
Skip bool
}
// NewKeyword registers a new keyword in database
func NewKeyword(keyword string) *Keyword {
keyword = strings.ToLower(strings.TrimSpace(keyword))
@ -24,6 +25,7 @@ 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()

View file

@ -11,7 +11,7 @@ import (
"github.com/photoprism/photoprism/pkg/txt"
)
// Labels for photo, album and location categorization
// Label is used for photo, album and location categorization
type Label struct {
ID uint `gorm:"primary_key"`
LabelUUID string `gorm:"type:varbinary(36);unique_index;"`
@ -28,6 +28,7 @@ type Label struct {
New bool `gorm:"-"`
}
// BeforeCreate computes a random UUID when a new label is created in database
func (m *Label) BeforeCreate(scope *gorm.Scope) error {
if err := scope.SetColumn("LabelUUID", rnd.PPID('l')); err != nil {
log.Errorf("label: %s", err)
@ -37,6 +38,7 @@ func (m *Label) BeforeCreate(scope *gorm.Scope) error {
return nil
}
// NewLabel creates a label in database with a given name and priority
func NewLabel(labelName string, labelPriority int) *Label {
labelName = strings.TrimSpace(labelName)
@ -55,6 +57,7 @@ func NewLabel(labelName string, labelPriority int) *Label {
return result
}
// FirstOrCreate checks wether the label already exists in the database
func (m *Label) FirstOrCreate(db *gorm.DB) *Label {
mutex.Db.Lock()
defer mutex.Db.Unlock()
@ -66,10 +69,12 @@ func (m *Label) FirstOrCreate(db *gorm.DB) *Label {
return m
}
// AfterCreate sets the New column used for database callback
func (m *Label) AfterCreate(scope *gorm.Scope) error {
return scope.SetColumn("New", true)
}
// Rename an existing label
func (m *Label) Rename(name string) {
name = txt.Clip(name, 128)

View file

@ -9,7 +9,7 @@ import (
"github.com/photoprism/photoprism/internal/mutex"
)
// Camera lens (as extracted from UpdateExif metadata)
// Lens represents camera lens (as extracted from UpdateExif metadata)
type Lens struct {
ID uint `gorm:"primary_key"`
LensSlug string `gorm:"type:varbinary(128);unique_index;"`
@ -24,10 +24,12 @@ type Lens struct {
DeletedAt *time.Time `sql:"index"`
}
// TableName returns Lens table identifier "lens"
func (Lens) TableName() string {
return "lenses"
}
// NewLens creates a new lens in database
func NewLens(modelName string, makeName string) *Lens {
modelName = strings.TrimSpace(modelName)
makeName = strings.TrimSpace(makeName)
@ -47,6 +49,7 @@ func NewLens(modelName string, makeName string) *Lens {
return result
}
// FirstOrCreate checks wether the lens already exists in the database
func (m *Lens) FirstOrCreate(db *gorm.DB) *Lens {
mutex.Db.Lock()
defer mutex.Db.Unlock()

View file

@ -14,7 +14,7 @@ import (
var locationMutex = sync.Mutex{}
// Photo location
// Location used to associate photos to location
type Location struct {
ID string `gorm:"type:varbinary(16);primary_key;auto_increment:false;"`
PlaceID string `gorm:"type:varbinary(16);"`
@ -26,16 +26,17 @@ type Location struct {
UpdatedAt time.Time
}
// Locks locations for updates
// Lock location for updates
func (Location) Lock() {
locationMutex.Lock()
}
// Unlock locations for updates
// Unlock location for updates
func (Location) Unlock() {
locationMutex.Unlock()
}
// NewLocation creates a location using a token extracted from coordinate
func NewLocation(lat, lng float64) *Location {
result := &Location{}
@ -44,6 +45,7 @@ func NewLocation(lat, lng float64) *Location {
return result
}
// 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 {
mutex.Db.Lock()
defer mutex.Db.Unlock()
@ -83,6 +85,7 @@ func (m *Location) Find(db *gorm.DB, api string) error {
return nil
}
// Keywords computes keyword based on a Location
func (m *Location) Keywords() []string {
result := []string{
strings.ToLower(m.City()),
@ -98,66 +101,82 @@ func (m *Location) Keywords() []string {
return result
}
// Unknown checks if the location has no id
func (m *Location) Unknown() bool {
return m.ID == ""
}
// Name returns name of location
func (m *Location) Name() string {
return m.LocName
}
// NoName checks if the location has no name
func (m *Location) NoName() bool {
return m.LocName == ""
}
// Category returns the location category
func (m *Location) Category() string {
return m.LocCategory
}
// NoCategory checks id the location has no category
func (m *Location) NoCategory() bool {
return m.LocCategory == ""
}
// Label returns the location place label
func (m *Location) Label() string {
return m.Place.Label()
}
// City returns the location place city
func (m *Location) City() string {
return m.Place.City()
}
// LongCity checks if the city name is more than 16 char
func (m *Location) LongCity() bool {
return len(m.City()) > 16
}
// NoCity checks if the location has no city
func (m *Location) NoCity() bool {
return m.City() == ""
}
// CityContains checks if the location city contains the text string
func (m *Location) CityContains(text string) bool {
return strings.Contains(text, m.City())
}
// State returns the location place state
func (m *Location) State() string {
return m.Place.State()
}
// NoState checks if the location place has no state
func (m *Location) NoState() bool {
return m.Place.State() == ""
}
// CountryCode returns the location place country code
func (m *Location) CountryCode() string {
return m.Place.CountryCode()
}
// CountryName returns the location place country name
func (m *Location) CountryName() string {
return m.Place.CountryName()
}
// Notes returns the locations place notes
func (m *Location) Notes() string {
return m.Place.Notes()
}
// Source returns the source of location information
func (m *Location) Source() string {
return m.LocSource
}

View file

@ -11,7 +11,7 @@ import (
"github.com/ulule/deepcopier"
)
// Photo represents a photo that can have multiple image or sidecar files.
// Photo represents a photo, all its properties, and link to all its images and sidecar files.
type Photo struct {
ID uint `gorm:"primary_key"`
TakenAt time.Time `gorm:"type:datetime;index:idx_photos_taken_uuid;" json:"TakenAt"`
@ -76,6 +76,7 @@ func SavePhoto(model Photo, form form.Photo, db *gorm.DB) error {
return db.Save(&model).Error
}
// BeforeCreate computes a unique UUID, and set a default takenAt before indexing a new photo
func (m *Photo) BeforeCreate(scope *gorm.Scope) error {
if err := scope.SetColumn("PhotoUUID", rnd.PPID('p')); err != nil {
return err
@ -96,6 +97,7 @@ func (m *Photo) BeforeCreate(scope *gorm.Scope) error {
return nil
}
// BeforeSave ensures the existence of TakenAt properties before indexing or updating a photo
func (m *Photo) BeforeSave(scope *gorm.Scope) error {
if m.TakenAt.IsZero() || m.TakenAtLocal.IsZero() {
now := time.Now()
@ -112,6 +114,7 @@ func (m *Photo) BeforeSave(scope *gorm.Scope) error {
return nil
}
// IndexKeywords adds given keywords to the photo entry
func (m *Photo) IndexKeywords(keywords []string, db *gorm.DB) {
var keywordIds []uint
@ -145,6 +148,7 @@ func (m *Photo) IndexKeywords(keywords []string, db *gorm.DB) {
db.Where("photo_id = ? AND keyword_id NOT IN (?)", m.ID, keywordIds).Delete(&PhotoKeyword{})
}
// PreloadFiles prepares gorm scope to retrieve photo file
func (m *Photo) PreloadFiles(db *gorm.DB) {
q := db.NewScope(nil).DB().
Table("files").
@ -166,6 +170,7 @@ func (m *Photo) PreloadFiles(db *gorm.DB) {
logError(q.Scan(&m.Labels))
} */
// PreloadKeywords prepares gorm scope to retrieve photo keywords
func (m *Photo) PreloadKeywords(db *gorm.DB) {
q := db.NewScope(nil).DB().
Table("keywords").
@ -176,6 +181,7 @@ func (m *Photo) PreloadKeywords(db *gorm.DB) {
logError(q.Scan(&m.Keywords))
}
// PreloadAlbums prepares gorm scope to retrieve photo albums
func (m *Photo) PreloadAlbums(db *gorm.DB) {
q := db.NewScope(nil).DB().
Table("albums").
@ -187,6 +193,7 @@ func (m *Photo) PreloadAlbums(db *gorm.DB) {
logError(q.Scan(&m.Albums))
}
// PreloadMany prepares gorm scope to retrieve photo file, albums and keywords
func (m *Photo) PreloadMany(db *gorm.DB) {
m.PreloadFiles(db)
// m.PreloadLabels(db)
@ -194,54 +201,67 @@ func (m *Photo) PreloadMany(db *gorm.DB) {
m.PreloadAlbums(db)
}
// NoLocation checks if the photo has no location
func (m *Photo) NoLocation() bool {
return m.LocationID == ""
}
// HasLocation checks if the photo has a location
func (m *Photo) HasLocation() bool {
return m.LocationID != ""
}
// NoPlace checks if the photo has no Place
func (m *Photo) NoPlace() bool {
return len(m.PlaceID) < 2
}
// HasPlace checks if the photo has a Place
func (m *Photo) HasPlace() bool {
return len(m.PlaceID) >= 2
}
// NoTitle checks if the photo has no Title
func (m *Photo) NoTitle() bool {
return m.PhotoTitle == ""
}
// NoDescription checks if the photo has no Description
func (m *Photo) NoDescription() bool {
return m.PhotoDescription == ""
}
// NoNotes checks if the photo has no Notes
func (m *Photo) NoNotes() bool {
return m.PhotoNotes == ""
}
// NoArtist checks if the photo has no Artist
func (m *Photo) NoArtist() bool {
return m.PhotoArtist == ""
}
// NoCopyright checks if the photo has no Copyright
func (m *Photo) NoCopyright() bool {
return m.PhotoCopyright == ""
}
// NoSubject checks if the photo has no Subject
func (m *Photo) NoSubject() bool {
return m.PhotoSubject == ""
}
// NoKeywords checks if the photo has no Keywords
func (m *Photo) NoKeywords() bool {
return m.PhotoKeywords == ""
}
// NoCameraSerial checks if the photo has no CameraSerial
func (m *Photo) NoCameraSerial() bool {
return m.CameraSerial == ""
}
// HasTitle checks if the photo has a Title
func (m *Photo) HasTitle() bool {
return m.PhotoTitle != ""
}

View file

@ -7,7 +7,7 @@ import (
"github.com/photoprism/photoprism/internal/mutex"
)
// Photos can be added to multiple albums
// PhotoAlbum represents the many_to_many relation between Photo and Album
type PhotoAlbum struct {
PhotoUUID string `gorm:"type:varbinary(36);primary_key;auto_increment:false"`
AlbumUUID string `gorm:"type:varbinary(36);primary_key;auto_increment:false;index"`
@ -18,10 +18,12 @@ type PhotoAlbum struct {
Album *Album
}
// TableName returns PhotoAlbum table identifier "photos_albums"
func (PhotoAlbum) TableName() string {
return "photos_albums"
}
// NewPhotoAlbum registers an photo and album association using UUID
func NewPhotoAlbum(photoUUID, albumUUID string) *PhotoAlbum {
result := &PhotoAlbum{
PhotoUUID: photoUUID,
@ -31,6 +33,7 @@ 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()

View file

@ -5,15 +5,18 @@ import (
"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"`
KeywordID uint `gorm:"primary_key;auto_increment:false;index"`
}
// TableName returns PhotoKeyword table identifier "photos_keywords"
func (PhotoKeyword) TableName() string {
return "photos_keywords"
}
// NewPhotoKeyword registers a new PhotoKeyword relation
func NewPhotoKeyword(photoID, keywordID uint) *PhotoKeyword {
result := &PhotoKeyword{
PhotoID: photoID,
@ -23,6 +26,7 @@ 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()

View file

@ -5,7 +5,8 @@ import (
"github.com/photoprism/photoprism/internal/mutex"
)
// Photo labels are weighted by uncertainty (100 - confidence)
// PhotoLabel represents the many-to-many relation between Photo and label.
// Labels are weighted by uncertainty (100 - confidence)
type PhotoLabel struct {
PhotoID uint `gorm:"primary_key;auto_increment:false"`
LabelID uint `gorm:"primary_key;auto_increment:false;index"`
@ -15,14 +16,16 @@ type PhotoLabel struct {
Label *Label
}
// TableName returns PhotoLabel table identifier "photos_labels"
func (PhotoLabel) TableName() string {
return "photos_labels"
}
func NewPhotoLabel(photoId, labelId uint, uncertainty int, source string) *PhotoLabel {
// NewPhotoLabel registers a new PhotoLabel relation with an uncertainty and a source of label
func NewPhotoLabel(photoID, labelID uint, uncertainty int, source string) *PhotoLabel {
result := &PhotoLabel{
PhotoID: photoId,
LabelID: labelId,
PhotoID: photoID,
LabelID: labelID,
LabelUncertainty: uncertainty,
LabelSource: source,
}
@ -30,6 +33,7 @@ func NewPhotoLabel(photoId, labelId uint, uncertainty int, source string) *Photo
return result
}
// FirstOrCreate checks wether 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()

View file

@ -8,7 +8,7 @@ import (
"github.com/photoprism/photoprism/internal/mutex"
)
// Photo place
// Place used to associate photos to places
type Place struct {
ID string `gorm:"type:varbinary(16);primary_key;auto_increment:false;"`
LocLabel string `gorm:"type:varbinary(512);unique_index;"`
@ -22,16 +22,20 @@ type Place struct {
New bool `gorm:"-"`
}
// UnknownPlace is the default unknown place
var UnknownPlace = NewPlace("-", "Unknown", "Unknown", "Unknown", "zz")
// CreateUnknownPlace initializes default place in the database
func CreateUnknownPlace(db *gorm.DB) {
UnknownPlace.FirstOrCreate(db)
}
// AfterCreate sets the New column used for database callback
func (m *Place) AfterCreate(scope *gorm.Scope) error {
return scope.SetColumn("New", true)
}
// FindPlace returns place from a token
func FindPlace(token string, db *gorm.DB) *Place {
place := &Place{}
@ -42,6 +46,7 @@ func FindPlace(token string, db *gorm.DB) *Place {
return place
}
// FindPlaceByLabel returns a place from an id or a label
func FindPlaceByLabel(id string, label string, db *gorm.DB) *Place {
place := &Place{}
@ -52,6 +57,7 @@ func FindPlaceByLabel(id string, label string, db *gorm.DB) *Place {
return place
}
// NewPlace registers a new place in database
func NewPlace(id, label, city, state, countryCode string) *Place {
result := &Place{
ID: id,
@ -64,6 +70,7 @@ func NewPlace(id, label, city, state, countryCode string) *Place {
return result
}
// 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 {
return err
@ -72,6 +79,7 @@ func (m *Place) Find(db *gorm.DB) error {
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()
@ -83,30 +91,37 @@ func (m *Place) FirstOrCreate(db *gorm.DB) *Place {
return m
}
// NoID checks is the place has no id
func (m *Place) NoID() bool {
return m.ID == ""
}
// Label returns place label
func (m *Place) Label() string {
return m.LocLabel
}
// City returns place City
func (m *Place) City() string {
return m.LocCity
}
// State returns place State
func (m *Place) State() string {
return m.LocState
}
// CountryCode returns place CountryCode
func (m *Place) CountryCode() string {
return m.LocCountry
}
// CountryName returns place CountryName
func (m *Place) CountryName() string {
return maps.CountryNames[m.LocCountry]
}
// Notes returns place Notes
func (m *Place) Notes() string {
return m.LocNotes
}