Quality Score
diff --git a/frontend/src/model/photo.js b/frontend/src/model/photo.js
index 64b4f221b..677b9a74a 100644
--- a/frontend/src/model/photo.js
+++ b/frontend/src/model/photo.js
@@ -44,6 +44,7 @@ export const TypeJpeg = "jpg";
export const TypeImage = "image";
export const YearUnknown = -1;
export const MonthUnknown = -1;
+export const DayUnknown = -1;
export class Photo extends RestModel {
getDefaults() {
@@ -57,7 +58,6 @@ export class Photo extends RestModel {
TakenAt: "",
TakenAtLocal: "",
TakenSrc: "",
- TakenAcc: 0,
TimeZone: "",
Path: "",
Color: "",
@@ -86,6 +86,7 @@ export class Photo extends RestModel {
Country: "",
Year: YearUnknown,
Month: MonthUnknown,
+ Day: DayUnknown,
Details: {
Keywords: "",
Notes: "",
@@ -124,6 +125,48 @@ export class Photo extends RestModel {
};
}
+ isoDay() {
+ if(!this.Day || this.Day <= 0) {
+ return this.TakenAt.substr(8, 2);
+ }
+
+ return this.Day.toString().padStart(2, "0");
+ }
+
+ isoMonth() {
+ if(!this.Month || this.Month <= 0) {
+ return this.TakenAt.substr(5, 2);
+ }
+
+ return this.Month.toString().padStart(2, "0");
+ }
+
+ isoYear() {
+ if(!this.Year || this.Year <= 1000) {
+ return this.TakenAt.substr(0, 4);
+ }
+
+ return this.Year.toString();
+ }
+
+ isoDate(time) {
+ if(!this.isoYear()) {
+ return this.TakenAt;
+ }
+
+ let date = this.isoYear() + "-" + this.isoMonth() + "-" + this.isoDay();
+
+ if(!time) {
+ time = this.TakenAt.substr(11, 8);
+ }
+
+ return `${date}T${time}Z`;
+ }
+
+ utcDate() {
+ return DateTime.fromISO(this.TakenAt).toUTC();
+ }
+
baseName(truncate) {
let result = this.fileBase(this.FileName ? this.FileName : this.mainFile().Name);
diff --git a/frontend/src/resources/options.js b/frontend/src/resources/options.js
index f47e9c845..a5511e3c8 100644
--- a/frontend/src/resources/options.js
+++ b/frontend/src/resources/options.js
@@ -1,19 +1,60 @@
import {$gettext} from "common/vm";
import moment from "moment-timezone";
import {Info} from "luxon";
+import {config} from "../session";
export const TimeZones = () => moment.tz.names();
+export const Days = () => {
+ let result = [];
+
+ for (let i = 1; i <= 31; i++) {
+ result.push({"value": i, "text": i.toString().padStart(2, "0")});
+ }
+
+ result.push({"value": -1, "text": $gettext("Unknown")});
+
+ return result;
+};
+
+export const Years = () => {
+ let result = [];
+
+ const currentYear = new Date().getUTCFullYear();
+
+ for (let i = currentYear; i >= 1750; i--) {
+ result.push({"value": i, "text": i.toString().padStart(4, "0")});
+ }
+
+ result.push({"value": -1, "text": $gettext("Unknown")});
+
+ return result;
+};
+
+export const IndexedYears = () => {
+ let result = [];
+
+ if (config.values.years) {
+ for (let i = 0; i < config.values.years.length; i++) {
+ result.push({"value": parseInt(config.values.years[i]), "text": config.values.years[i].toString()});
+ }
+ }
+
+ result.push({"value": -1, "text": $gettext("Unknown")});
+
+ return result;
+};
+
export const Months = () => {
let result = [];
const months = Info.months("long");
for (let i = 0; i < months.length; i++) {
- result.push({"Month": i + 1, "Name": months[i]});
+ result.push({"value": i + 1, "text": months[i]});
}
- result.push({"Month": -1, "Name": $gettext("Unknown")});
+ result.push({"value": -1, "text": $gettext("Unknown")});
return result;
};
diff --git a/internal/entity/album.go b/internal/entity/album.go
index 43a853e00..50828f9b7 100644
--- a/internal/entity/album.go
+++ b/internal/entity/album.go
@@ -43,6 +43,7 @@ type Album struct {
AlbumCountry string `gorm:"type:varbinary(2);index:idx_albums_country_year_month;default:'zz'" json:"Country" yaml:"Country,omitempty"`
AlbumYear int `gorm:"index:idx_albums_country_year_month;" json:"Year" yaml:"Year,omitempty"`
AlbumMonth int `gorm:"index:idx_albums_country_year_month;" json:"Month" yaml:"Month,omitempty"`
+ AlbumDay int `json:"Day" yaml:"Day,omitempty"`
AlbumFavorite bool `json:"Favorite" yaml:"Favorite,omitempty"`
AlbumPrivate bool `json:"Private" yaml:"Private,omitempty"`
CreatedAt time.Time `json:"CreatedAt" yaml:"-"`
diff --git a/internal/entity/const.go b/internal/entity/const.go
index b3ba36e74..4e234890c 100644
--- a/internal/entity/const.go
+++ b/internal/entity/const.go
@@ -25,6 +25,7 @@ const (
// Unknown values.
YearUnknown = -1
MonthUnknown = -1
+ DayUnknown = -1
TitleUnknown = "Unknown"
// Content types.
diff --git a/internal/entity/person.go b/internal/entity/person.go
index 2733d18cf..ce68bd886 100644
--- a/internal/entity/person.go
+++ b/internal/entity/person.go
@@ -38,6 +38,9 @@ type Person struct {
CanDownload bool `json:"CanDownload" yaml:"CanDownload,omitempty"`
WebDAV bool `gorm:"column:webdav" json:"WebDAV" yaml:"WebDAV,omitempty"`
ApiToken string `json:"ApiToken" yaml:"ApiToken,omitempty"`
+ BirthYear int `json:"BirthYear" yaml:"BirthYear,omitempty"`
+ BirthMonth int `json:"BirthMonth" yaml:"BirthMonth,omitempty"`
+ BirthDay int `json:"BirthDay" yaml:"BirthDay,omitempty"`
LoginAttempts int `json:"-" yaml:"-,omitempty"`
LoginAt *time.Time `json:"-" yaml:"-"`
CreatedAt time.Time `json:"CreatedAt" yaml:"-"`
diff --git a/internal/entity/photo.go b/internal/entity/photo.go
index a792ec7ba..9e0a2b815 100644
--- a/internal/entity/photo.go
+++ b/internal/entity/photo.go
@@ -37,7 +37,6 @@ type Photo struct {
TakenAt time.Time `gorm:"type:datetime;index:idx_photos_taken_uid;" json:"TakenAt" yaml:"TakenAt"`
TakenAtLocal time.Time `gorm:"type:datetime;" yaml:"-"`
TakenSrc string `gorm:"type:varbinary(8);" json:"TakenSrc" yaml:"TakenSrc,omitempty"`
- TakenAcc int `json:"TakenAcc" yaml:"TakenAcc,omitempty"`
PhotoUID string `gorm:"type:varbinary(42);unique_index;index:idx_photos_taken_uid;" json:"UID" yaml:"UID"`
PhotoType string `gorm:"type:varbinary(8);default:'image';" json:"Type" yaml:"Type"`
PhotoTitle string `gorm:"type:varchar(255);" json:"Title" yaml:"Title"`
@@ -60,8 +59,9 @@ type Photo struct {
PhotoLng float32 `gorm:"type:FLOAT;index;" json:"Lng" yaml:"Lng,omitempty"`
PhotoAltitude int `json:"Altitude" yaml:"Altitude,omitempty"`
PhotoCountry string `gorm:"type:varbinary(2);index:idx_photos_country_year_month;default:'zz'" json:"Country" yaml:"-"`
- PhotoYear int `gorm:"index:idx_photos_country_year_month;" json:"Year" yaml:"-"`
- PhotoMonth int `gorm:"index:idx_photos_country_year_month;" json:"Month" yaml:"-"`
+ PhotoYear int `gorm:"index:idx_photos_country_year_month;" json:"Year" yaml:"Year"`
+ PhotoMonth int `gorm:"index:idx_photos_country_year_month;" json:"Month" yaml:"Month"`
+ PhotoDay int `json:"Day" yaml:"Day"`
PhotoIso int `json:"Iso" yaml:"ISO,omitempty"`
PhotoExposure string `gorm:"type:varbinary(64);" json:"Exposure" yaml:"Exposure,omitempty"`
PhotoFNumber float32 `gorm:"type:FLOAT;" json:"FNumber" yaml:"FNumber,omitempty"`
@@ -111,7 +111,7 @@ func SavePhotoForm(model Photo, form form.Photo, geoApi string) error {
return errors.New("photo: can't save form, id is empty")
}
- model.UpdateYearMonth()
+ model.UpdateDateFields()
if form.Details.PhotoID == model.ID {
if err := deepcopier.Copy(&model.Details).From(form.Details); err != nil {
@@ -182,7 +182,7 @@ func (m *Photo) Save() error {
labels := m.ClassifyLabels()
- m.UpdateYearMonth()
+ m.UpdateDateFields()
if err := m.UpdateTitle(labels); err != nil {
log.Info(err)
@@ -735,11 +735,11 @@ func (m *Photo) SetTakenAt(taken, local time.Time, zone, source string) {
m.TimeZone = zone
}
- m.UpdateYearMonth()
+ m.UpdateDateFields()
}
-// UpdateYearMonth updates internal date fields.
-func (m *Photo) UpdateYearMonth() {
+// UpdateDateFields updates internal date fields.
+func (m *Photo) UpdateDateFields() {
if m.TakenAt.IsZero() || m.TakenAt.Year() < 1000 {
return
}
@@ -751,9 +751,11 @@ func (m *Photo) UpdateYearMonth() {
if m.TakenSrc == SrcAuto {
m.PhotoYear = YearUnknown
m.PhotoMonth = MonthUnknown
- } else {
+ m.PhotoDay = DayUnknown
+ } else if m.TakenSrc != SrcManual {
m.PhotoYear = m.TakenAtLocal.Year()
m.PhotoMonth = int(m.TakenAtLocal.Month())
+ m.PhotoDay = m.TakenAtLocal.Day()
}
}
diff --git a/internal/entity/photo_optimize.go b/internal/entity/photo_optimize.go
index ac27d221d..281e569b0 100644
--- a/internal/entity/photo_optimize.go
+++ b/internal/entity/photo_optimize.go
@@ -105,7 +105,7 @@ func (m *Photo) Optimize() (updated bool, err error) {
labels := m.ClassifyLabels()
- m.UpdateYearMonth()
+ m.UpdateDateFields()
if err := m.UpdateTitle(labels); err != nil {
log.Info(err)
diff --git a/internal/form/album_search.go b/internal/form/album_search.go
index 011aa293e..f4eedfde8 100644
--- a/internal/form/album_search.go
+++ b/internal/form/album_search.go
@@ -11,6 +11,7 @@ type AlbumSearch struct {
Country string `json:"country"`
Year int `json:"year"`
Month int `json:"month"`
+ Day int `json:"day"`
Favorite bool `form:"favorite"`
Private bool `form:"private"`
Count int `form:"count" binding:"required" serialize:"-"`
diff --git a/internal/form/photo.go b/internal/form/photo.go
index a28324647..1ffb55afe 100644
--- a/internal/form/photo.go
+++ b/internal/form/photo.go
@@ -22,8 +22,10 @@ type Photo struct {
TakenAt time.Time `json:"TakenAt"`
TakenAtLocal time.Time `json:"TakenAtLocal"`
TakenSrc string `json:"TakenSrc"`
- TakenAcc int `json:"TakenAcc"`
TimeZone string `json:"TimeZone"`
+ PhotoYear int `json:"Year"`
+ PhotoMonth int `json:"Month"`
+ PhotoDay int `json:"Day"`
PhotoTitle string `json:"Title"`
TitleSrc string `json:"TitleSrc"`
PhotoDescription string `json:"Description"`
diff --git a/internal/form/photo_search.go b/internal/form/photo_search.go
index 9079d3be2..60541b45c 100644
--- a/internal/form/photo_search.go
+++ b/internal/form/photo_search.go
@@ -46,6 +46,7 @@ type PhotoSearch struct {
State string `form:"state"` // Moments
Year int `form:"year"` // Moments
Month int `form:"month"` // Moments
+ Day int `form:"day"` // Moments
Color string `form:"color"`
Quality int `form:"quality"`
Review bool `form:"review"`
diff --git a/internal/photoprism/index_mediafile.go b/internal/photoprism/index_mediafile.go
index 55e23f6ea..a3f41fa83 100644
--- a/internal/photoprism/index_mediafile.go
+++ b/internal/photoprism/index_mediafile.go
@@ -466,7 +466,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
photo.PlaceID = entity.UnknownPlace.ID
}
- photo.UpdateYearMonth()
+ photo.UpdateDateFields()
file.FileSidecar = m.IsSidecar()
file.FileVideo = m.IsVideo()
diff --git a/internal/query/albums.go b/internal/query/albums.go
index 6dffc05d5..14c86ecb3 100644
--- a/internal/query/albums.go
+++ b/internal/query/albums.go
@@ -8,6 +8,7 @@ import (
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/pkg/capture"
+ "github.com/photoprism/photoprism/pkg/txt"
)
// AlbumResult contains found albums
@@ -29,6 +30,7 @@ type AlbumResult struct {
AlbumCountry string `json:"Country"`
AlbumYear int `json:"Year"`
AlbumMonth int `json:"Month"`
+ AlbumDay int `json:"Day"`
AlbumFavorite bool `json:"Favorite"`
AlbumPrivate bool `json:"Private"`
PhotoCount int `json:"PhotoCount"`
@@ -138,11 +140,23 @@ func AlbumSearch(f form.AlbumSearch) (results AlbumResults, err error) {
s = s.Where("albums.album_favorite = 1")
}
+ if (f.Year > 0 && f.Year <= txt.YearMax) || f.Year == entity.YearUnknown {
+ s = s.Where("albums.album_year = ?", f.Year)
+ }
+
+ if (f.Month >= txt.MonthMin && f.Month <= txt.MonthMax) || f.Month == entity.MonthUnknown {
+ s = s.Where("albums.album_month = ?", f.Month)
+ }
+
+ if (f.Day >= txt.DayMin && f.Month <= txt.DayMax) || f.Day == entity.DayUnknown {
+ s = s.Where("albums.album_day = ?", f.Day)
+ }
+
switch f.Order {
case "slug":
s = s.Order("albums.album_favorite DESC, album_slug ASC")
default:
- s = s.Order("albums.album_favorite DESC, albums.album_year DESC, albums.album_month DESC, albums.album_title, albums.created_at DESC")
+ s = s.Order("albums.album_favorite DESC, albums.album_year DESC, albums.album_month DESC, albums.album_day DESC, albums.album_title, albums.created_at DESC")
}
if f.Count > 0 && f.Count <= MaxResults {
diff --git a/internal/query/photo_results.go b/internal/query/photo_results.go
index cb67df1f3..aee04830a 100644
--- a/internal/query/photo_results.go
+++ b/internal/query/photo_results.go
@@ -20,7 +20,6 @@ type PhotoResult struct {
TakenAt time.Time `json:"TakenAt"`
TakenAtLocal time.Time `json:"TakenAtLocal"`
TakenSrc string `json:"TakenSrc"`
- TakenAcc int `json:"TakenAcc"`
TimeZone string `json:"TimeZone"`
PhotoPath string `json:"Path"`
PhotoName string `json:"Name"`
@@ -29,6 +28,7 @@ type PhotoResult struct {
PhotoDescription string `json:"Description"`
PhotoYear int `json:"Year"`
PhotoMonth int `json:"Month"`
+ PhotoDay int `json:"Day"`
PhotoCountry string `json:"Country"`
PhotoFavorite bool `json:"Favorite"`
PhotoPrivate bool `json:"Private"`
diff --git a/internal/query/photo_search.go b/internal/query/photo_search.go
index 27e50f543..cc855d1d1 100644
--- a/internal/query/photo_search.go
+++ b/internal/query/photo_search.go
@@ -178,6 +178,10 @@ func PhotoSearch(f form.PhotoSearch) (results PhotoResults, count int, err error
s = s.Where("photos.photo_month = ?", f.Month)
}
+ if (f.Day >= txt.DayMin && f.Month <= txt.DayMax) || f.Day == entity.DayUnknown {
+ s = s.Where("photos.photo_day = ?", f.Day)
+ }
+
if f.Color != "" {
s = s.Where("files.file_main_color IN (?)", strings.Split(strings.ToLower(f.Color), ","))
}
|