mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-12 00:34:13 +01:00
Labels: Edit name in overview #212
Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
@@ -61,13 +61,12 @@
|
|||||||
<v-card tile class="accent lighten-3"
|
<v-card tile class="accent lighten-3"
|
||||||
slot-scope="{ hover }"
|
slot-scope="{ hover }"
|
||||||
:class="selection.includes(album.AlbumUUID) ? 'elevation-10 ma-0' : 'elevation-0 ma-1'"
|
:class="selection.includes(album.AlbumUUID) ? 'elevation-10 ma-0' : 'elevation-0 ma-1'"
|
||||||
|
:to="{name: 'album', params: {uuid: album.AlbumUUID, slug: album.AlbumSlug}}"
|
||||||
>
|
>
|
||||||
<v-img
|
<v-img
|
||||||
:src="album.getThumbnailUrl('tile_500')"
|
:src="album.getThumbnailUrl('tile_500')"
|
||||||
aspect-ratio="1"
|
aspect-ratio="1"
|
||||||
style="cursor: pointer"
|
|
||||||
class="accent lighten-2"
|
class="accent lighten-2"
|
||||||
@click.prevent="openAlbum(index)"
|
|
||||||
>
|
>
|
||||||
<v-layout
|
<v-layout
|
||||||
slot="placeholder"
|
slot="placeholder"
|
||||||
@@ -90,7 +89,7 @@
|
|||||||
</v-btn>
|
</v-btn>
|
||||||
</v-img>
|
</v-img>
|
||||||
|
|
||||||
<v-card-actions>
|
<v-card-actions @click.stop.prevent="">
|
||||||
<v-edit-dialog
|
<v-edit-dialog
|
||||||
:return-value.sync="album.AlbumName"
|
:return-value.sync="album.AlbumName"
|
||||||
lazy
|
lazy
|
||||||
@@ -183,10 +182,6 @@
|
|||||||
this.filter.q = '';
|
this.filter.q = '';
|
||||||
this.search();
|
this.search();
|
||||||
},
|
},
|
||||||
openAlbum(index) {
|
|
||||||
const album = this.results[index];
|
|
||||||
this.$router.push({name: "album", params: {uuid: album.AlbumUUID, slug: album.AlbumSlug}});
|
|
||||||
},
|
|
||||||
loadMore() {
|
loadMore() {
|
||||||
if (this.scrollDisabled) return;
|
if (this.scrollDisabled) return;
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
</v-form>
|
</v-form>
|
||||||
|
|
||||||
<v-container fluid class="pa-4" v-if="loading">
|
<v-container fluid class="pa-4" v-if="loading">
|
||||||
<v-progress-linear color="secondary-dark" :indeterminate="true"></v-progress-linear>
|
<v-progress-linear color="secondary-dark" :indeterminate="true"></v-progress-linear>
|
||||||
</v-container>
|
</v-container>
|
||||||
<v-container fluid class="pa-0" v-else>
|
<v-container fluid class="pa-0" v-else>
|
||||||
<p-scroll-top></p-scroll-top>
|
<p-scroll-top></p-scroll-top>
|
||||||
@@ -42,8 +42,12 @@
|
|||||||
<v-card v-if="results.length === 0" class="p-labels-empty secondary-light lighten-1" flat>
|
<v-card v-if="results.length === 0" class="p-labels-empty secondary-light lighten-1" flat>
|
||||||
<v-card-title primary-title>
|
<v-card-title primary-title>
|
||||||
<div>
|
<div>
|
||||||
<h3 class="title mb-3"><translate>No labels matched your search</translate></h3>
|
<h3 class="title mb-3">
|
||||||
<div><translate>Try again using a related or otherwise similar term.</translate></div>
|
<translate>No labels matched your search</translate>
|
||||||
|
</h3>
|
||||||
|
<div>
|
||||||
|
<translate>Try again using a related or otherwise similar term.</translate>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
</v-card>
|
</v-card>
|
||||||
@@ -55,13 +59,12 @@
|
|||||||
xs6 sm4 md3 lg2 d-flex
|
xs6 sm4 md3 lg2 d-flex
|
||||||
>
|
>
|
||||||
<v-hover>
|
<v-hover>
|
||||||
<v-card tile class="elevation-0 ma-1 accent lighten-3">
|
<v-card tile class="elevation-0 ma-1 accent lighten-3"
|
||||||
|
:to="{name: 'photos', query: {q: 'label:' + label.LabelSlug}}">
|
||||||
<v-img
|
<v-img
|
||||||
:src="label.getThumbnailUrl('tile_500')"
|
:src="label.getThumbnailUrl('tile_500')"
|
||||||
aspect-ratio="1"
|
aspect-ratio="1"
|
||||||
style="cursor: pointer"
|
|
||||||
class="accent lighten-2"
|
class="accent lighten-2"
|
||||||
@click.prevent="openLabel(index)"
|
|
||||||
>
|
>
|
||||||
<v-layout
|
<v-layout
|
||||||
slot="placeholder"
|
slot="placeholder"
|
||||||
@@ -70,12 +73,35 @@
|
|||||||
justify-center
|
justify-center
|
||||||
ma-0
|
ma-0
|
||||||
>
|
>
|
||||||
<v-progress-circular indeterminate color="accent lighten-5"></v-progress-circular>
|
<v-progress-circular indeterminate
|
||||||
|
color="accent lighten-5"></v-progress-circular>
|
||||||
</v-layout>
|
</v-layout>
|
||||||
</v-img>
|
</v-img>
|
||||||
|
|
||||||
<v-card-actions>
|
<v-card-actions @click.stop.prevent="">
|
||||||
{{ label.LabelName | capitalize }}
|
<v-edit-dialog
|
||||||
|
:return-value.sync="label.LabelName"
|
||||||
|
lazy
|
||||||
|
@save="onSave(label)"
|
||||||
|
class="p-inline-edit"
|
||||||
|
>
|
||||||
|
<span v-if="label.LabelName">
|
||||||
|
{{ label.LabelName | capitalize }}
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
<v-icon>edit</v-icon>
|
||||||
|
</span>
|
||||||
|
<template v-slot:input>
|
||||||
|
<v-text-field
|
||||||
|
v-model="label.LabelName"
|
||||||
|
:rules="[titleRule]"
|
||||||
|
:label="labels.name"
|
||||||
|
color="secondary-dark"
|
||||||
|
single-line
|
||||||
|
autofocus
|
||||||
|
></v-text-field>
|
||||||
|
</template>
|
||||||
|
</v-edit-dialog>
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
<v-btn icon @click.stop.prevent="label.toggleLike()">
|
<v-btn icon @click.stop.prevent="label.toggleLike()">
|
||||||
<v-icon v-if="label.LabelFavorite" color="#FFD600">star
|
<v-icon v-if="label.LabelFavorite" color="#FFD600">star
|
||||||
@@ -132,10 +158,15 @@
|
|||||||
routeName: routeName,
|
routeName: routeName,
|
||||||
labels: {
|
labels: {
|
||||||
search: this.$gettext("Search"),
|
search: this.$gettext("Search"),
|
||||||
|
name: this.$gettext("Label Name"),
|
||||||
},
|
},
|
||||||
|
titleRule: v => v.length <= 25 || this.$gettext("Name too long"),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
onSave(label) {
|
||||||
|
label.update();
|
||||||
|
},
|
||||||
showAll() {
|
showAll() {
|
||||||
this.filter.all = "true";
|
this.filter.all = "true";
|
||||||
this.updateQuery();
|
this.updateQuery();
|
||||||
@@ -148,10 +179,6 @@
|
|||||||
this.filter.q = '';
|
this.filter.q = '';
|
||||||
this.updateQuery();
|
this.updateQuery();
|
||||||
},
|
},
|
||||||
openLabel(index) {
|
|
||||||
const label = this.results[index];
|
|
||||||
this.$router.push({name: "photos", query: {q: "label:" + label.LabelSlug}});
|
|
||||||
},
|
|
||||||
loadMore() {
|
loadMore() {
|
||||||
if (this.scrollDisabled) return;
|
if (this.scrollDisabled) return;
|
||||||
|
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ func UpdateAlbum(router *gin.RouterGroup, conf *config.Config) {
|
|||||||
conf.Db().Save(&m)
|
conf.Db().Save(&m)
|
||||||
|
|
||||||
event.Publish("config.updated", event.Data(conf.ClientConfig()))
|
event.Publish("config.updated", event.Data(conf.ClientConfig()))
|
||||||
event.Success(fmt.Sprintf("album \"%s\" saved", m.AlbumName))
|
event.Success("album saved")
|
||||||
|
|
||||||
PublishAlbumEvent(EntityUpdated, id, c, q)
|
PublishAlbumEvent(EntityUpdated, id, c, q)
|
||||||
|
|
||||||
|
|||||||
@@ -14,5 +14,6 @@ var (
|
|||||||
ErrUploadNSFW = gin.H{"code": http.StatusForbidden, "error": txt.UcFirst(config.ErrUploadNSFW.Error())}
|
ErrUploadNSFW = gin.H{"code": http.StatusForbidden, "error": txt.UcFirst(config.ErrUploadNSFW.Error())}
|
||||||
ErrAlbumNotFound = gin.H{"code": http.StatusNotFound, "error": "Album not found"}
|
ErrAlbumNotFound = gin.H{"code": http.StatusNotFound, "error": "Album not found"}
|
||||||
ErrPhotoNotFound = gin.H{"code": http.StatusNotFound, "error": "Photo not found"}
|
ErrPhotoNotFound = gin.H{"code": http.StatusNotFound, "error": "Photo not found"}
|
||||||
|
ErrLabelNotFound = gin.H{"code": http.StatusNotFound, "error": "Label not found"}
|
||||||
ErrUnexpectedError = gin.H{"code": http.StatusInternalServerError, "error": "Unexpected error"}
|
ErrUnexpectedError = gin.H{"code": http.StatusInternalServerError, "error": "Unexpected error"}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -50,6 +50,42 @@ func GetLabels(router *gin.RouterGroup, conf *config.Config) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PUT /api/v1/labels/:uuid
|
||||||
|
func UpdateLabel(router *gin.RouterGroup, conf *config.Config) {
|
||||||
|
router.PUT("/labels/:uuid", func(c *gin.Context) {
|
||||||
|
if Unauthorized(c, conf) {
|
||||||
|
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var f form.Label
|
||||||
|
|
||||||
|
if err := c.BindJSON(&f); err != nil {
|
||||||
|
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
id := c.Param("uuid")
|
||||||
|
q := query.New(conf.OriginalsPath(), conf.Db())
|
||||||
|
|
||||||
|
m, err := q.FindLabelByUUID(id)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithStatusJSON(http.StatusNotFound, ErrLabelNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Rename(f.LabelName)
|
||||||
|
conf.Db().Save(&m)
|
||||||
|
|
||||||
|
event.Success("label saved")
|
||||||
|
|
||||||
|
PublishLabelEvent(EntityUpdated, id, c, q)
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, m)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// POST /api/v1/labels/:uuid/like
|
// POST /api/v1/labels/:uuid/like
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ var rules = LabelRules{
|
|||||||
},
|
},
|
||||||
"african chameleon": {
|
"african chameleon": {
|
||||||
Label: "chameleon",
|
Label: "chameleon",
|
||||||
Threshold: 0.500000,
|
Threshold: 0.900000,
|
||||||
Priority: 1,
|
Priority: 1,
|
||||||
Categories: []string{"reptile", "animal", "lizard"},
|
Categories: []string{"reptile", "animal", "lizard"},
|
||||||
},
|
},
|
||||||
@@ -148,9 +148,9 @@ var rules = LabelRules{
|
|||||||
},
|
},
|
||||||
"altar": {
|
"altar": {
|
||||||
Label: "",
|
Label: "",
|
||||||
Threshold: 0.300000,
|
Threshold: 0.500000,
|
||||||
Priority: 0,
|
Priority: 0,
|
||||||
Categories: []string{},
|
Categories: []string{"church"},
|
||||||
},
|
},
|
||||||
"ambulance": {
|
"ambulance": {
|
||||||
Label: "van",
|
Label: "van",
|
||||||
@@ -172,7 +172,7 @@ var rules = LabelRules{
|
|||||||
},
|
},
|
||||||
"american chameleon": {
|
"american chameleon": {
|
||||||
Label: "chameleon",
|
Label: "chameleon",
|
||||||
Threshold: 0.500000,
|
Threshold: 0.900000,
|
||||||
Priority: 1,
|
Priority: 1,
|
||||||
Categories: []string{"reptile", "animal", "lizard"},
|
Categories: []string{"reptile", "animal", "lizard"},
|
||||||
},
|
},
|
||||||
@@ -520,7 +520,7 @@ var rules = LabelRules{
|
|||||||
},
|
},
|
||||||
"beacon": {
|
"beacon": {
|
||||||
Label: "",
|
Label: "",
|
||||||
Threshold: 0.000000,
|
Threshold: 0.400000,
|
||||||
Priority: 0,
|
Priority: 0,
|
||||||
Categories: []string{"tower", "architecture"},
|
Categories: []string{"tower", "architecture"},
|
||||||
},
|
},
|
||||||
@@ -664,8 +664,8 @@ var rules = LabelRules{
|
|||||||
},
|
},
|
||||||
"binoculars": {
|
"binoculars": {
|
||||||
Label: "",
|
Label: "",
|
||||||
Threshold: 0.500000,
|
Threshold: 1.000000,
|
||||||
Priority: 0,
|
Priority: -2,
|
||||||
Categories: []string{},
|
Categories: []string{},
|
||||||
},
|
},
|
||||||
"bird": {
|
"bird": {
|
||||||
@@ -729,10 +729,10 @@ var rules = LabelRules{
|
|||||||
Categories: []string{"animal"},
|
Categories: []string{"animal"},
|
||||||
},
|
},
|
||||||
"black-footed ferret": {
|
"black-footed ferret": {
|
||||||
Label: "",
|
Label: "animal",
|
||||||
Threshold: 0.400000,
|
Threshold: 0.400000,
|
||||||
Priority: 0,
|
Priority: 0,
|
||||||
Categories: []string{"animal"},
|
Categories: []string{},
|
||||||
},
|
},
|
||||||
"blenheim spaniel dog": {
|
"blenheim spaniel dog": {
|
||||||
Label: "dog",
|
Label: "dog",
|
||||||
@@ -855,9 +855,9 @@ var rules = LabelRules{
|
|||||||
Categories: []string{"animal"},
|
Categories: []string{"animal"},
|
||||||
},
|
},
|
||||||
"bow": {
|
"bow": {
|
||||||
Label: "portrait",
|
Label: "",
|
||||||
Threshold: 0.200000,
|
Threshold: 1.000000,
|
||||||
Priority: 0,
|
Priority: -2,
|
||||||
Categories: []string{},
|
Categories: []string{},
|
||||||
},
|
},
|
||||||
"bow tie": {
|
"bow tie": {
|
||||||
@@ -1240,7 +1240,7 @@ var rules = LabelRules{
|
|||||||
},
|
},
|
||||||
"chameleon": {
|
"chameleon": {
|
||||||
Label: "chameleon",
|
Label: "chameleon",
|
||||||
Threshold: 0.500000,
|
Threshold: 0.900000,
|
||||||
Priority: 1,
|
Priority: 1,
|
||||||
Categories: []string{"reptile", "animal", "lizard"},
|
Categories: []string{"reptile", "animal", "lizard"},
|
||||||
},
|
},
|
||||||
@@ -1731,10 +1731,10 @@ var rules = LabelRules{
|
|||||||
Categories: []string{"reptile", "animal"},
|
Categories: []string{"reptile", "animal"},
|
||||||
},
|
},
|
||||||
"diaper": {
|
"diaper": {
|
||||||
Label: "baby",
|
Label: "",
|
||||||
Threshold: 0.250000,
|
Threshold: 1.000000,
|
||||||
Priority: 0,
|
Priority: -2,
|
||||||
Categories: []string{"people"},
|
Categories: []string{},
|
||||||
},
|
},
|
||||||
"digital clock": {
|
"digital clock": {
|
||||||
Label: "display",
|
Label: "display",
|
||||||
@@ -3621,10 +3621,10 @@ var rules = LabelRules{
|
|||||||
Categories: []string{"vehicle"},
|
Categories: []string{"vehicle"},
|
||||||
},
|
},
|
||||||
"mink": {
|
"mink": {
|
||||||
Label: "",
|
Label: "animal",
|
||||||
Threshold: 0.400000,
|
Threshold: 0.400000,
|
||||||
Priority: 0,
|
Priority: 0,
|
||||||
Categories: []string{"animal"},
|
Categories: []string{},
|
||||||
},
|
},
|
||||||
"missile": {
|
"missile": {
|
||||||
Label: "",
|
Label: "",
|
||||||
@@ -4293,10 +4293,10 @@ var rules = LabelRules{
|
|||||||
Categories: []string{},
|
Categories: []string{},
|
||||||
},
|
},
|
||||||
"polecat": {
|
"polecat": {
|
||||||
Label: "",
|
Label: "animal",
|
||||||
Threshold: 0.400000,
|
Threshold: 0.400000,
|
||||||
Priority: 0,
|
Priority: 0,
|
||||||
Categories: []string{"animal"},
|
Categories: []string{},
|
||||||
},
|
},
|
||||||
"police van": {
|
"police van": {
|
||||||
Label: "van",
|
Label: "van",
|
||||||
|
|||||||
@@ -417,7 +417,7 @@ common iguana:
|
|||||||
|
|
||||||
chameleon:
|
chameleon:
|
||||||
label: chameleon
|
label: chameleon
|
||||||
threshold: 0.5
|
threshold: 0.9
|
||||||
priority: 1
|
priority: 1
|
||||||
categories:
|
categories:
|
||||||
- reptile
|
- reptile
|
||||||
@@ -1578,19 +1578,16 @@ weasel:
|
|||||||
- animal
|
- animal
|
||||||
|
|
||||||
mink:
|
mink:
|
||||||
|
label: animal
|
||||||
threshold: 0.4
|
threshold: 0.4
|
||||||
categories:
|
|
||||||
- animal
|
|
||||||
|
|
||||||
polecat:
|
polecat:
|
||||||
|
label: animal
|
||||||
threshold: 0.4
|
threshold: 0.4
|
||||||
categories:
|
|
||||||
- animal
|
|
||||||
|
|
||||||
black-footed ferret:
|
black-footed ferret:
|
||||||
|
label: animal
|
||||||
threshold: 0.4
|
threshold: 0.4
|
||||||
categories:
|
|
||||||
- animal
|
|
||||||
|
|
||||||
otter:
|
otter:
|
||||||
threshold: 0.4
|
threshold: 0.4
|
||||||
@@ -1873,6 +1870,7 @@ bathtub:
|
|||||||
see: living
|
see: living
|
||||||
|
|
||||||
beacon:
|
beacon:
|
||||||
|
threshold: 0.4
|
||||||
categories:
|
categories:
|
||||||
- tower
|
- tower
|
||||||
- architecture
|
- architecture
|
||||||
@@ -3603,7 +3601,7 @@ bassinet:
|
|||||||
see: baby
|
see: baby
|
||||||
|
|
||||||
diaper:
|
diaper:
|
||||||
see: baby
|
see: ignore
|
||||||
|
|
||||||
patio:
|
patio:
|
||||||
label: building
|
label: building
|
||||||
@@ -3693,7 +3691,9 @@ sundial:
|
|||||||
threshold: 0.5
|
threshold: 0.5
|
||||||
|
|
||||||
altar:
|
altar:
|
||||||
threshold: 0.3
|
threshold: 0.5
|
||||||
|
categories:
|
||||||
|
- church
|
||||||
|
|
||||||
ignore:
|
ignore:
|
||||||
threshold: 1
|
threshold: 1
|
||||||
@@ -3777,11 +3777,10 @@ barometer:
|
|||||||
see: display
|
see: display
|
||||||
|
|
||||||
binoculars:
|
binoculars:
|
||||||
threshold: 0.5
|
see: ignore
|
||||||
|
|
||||||
bow:
|
bow:
|
||||||
label: portrait
|
see: ignore
|
||||||
threshold: 0.2
|
|
||||||
|
|
||||||
caldron:
|
caldron:
|
||||||
label: cooking
|
label: cooking
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/jinzhu/gorm"
|
"github.com/jinzhu/gorm"
|
||||||
"github.com/photoprism/photoprism/internal/mutex"
|
"github.com/photoprism/photoprism/internal/mutex"
|
||||||
"github.com/photoprism/photoprism/pkg/rnd"
|
"github.com/photoprism/photoprism/pkg/rnd"
|
||||||
|
"github.com/photoprism/photoprism/pkg/txt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Labels for photo, album and location categorization
|
// Labels for photo, album and location categorization
|
||||||
@@ -68,3 +69,13 @@ func (m *Label) FirstOrCreate(db *gorm.DB) *Label {
|
|||||||
func (m *Label) AfterCreate(scope *gorm.Scope) error {
|
func (m *Label) AfterCreate(scope *gorm.Scope) error {
|
||||||
return scope.SetColumn("New", true)
|
return scope.SetColumn("New", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Label) Rename(name string) {
|
||||||
|
name = txt.Clip(name, 128)
|
||||||
|
|
||||||
|
if name == "" {
|
||||||
|
name = txt.SlugToTitle(m.LabelSlug)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.LabelName = name
|
||||||
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ func registerRoutes(router *gin.Engine, conf *config.Config) {
|
|||||||
api.GetMomentsTime(v1, conf)
|
api.GetMomentsTime(v1, conf)
|
||||||
|
|
||||||
api.GetLabels(v1, conf)
|
api.GetLabels(v1, conf)
|
||||||
|
api.UpdateLabel(v1, conf)
|
||||||
api.LikeLabel(v1, conf)
|
api.LikeLabel(v1, conf)
|
||||||
api.DislikeLabel(v1, conf)
|
api.DislikeLabel(v1, conf)
|
||||||
api.LabelThumbnail(v1, conf)
|
api.LabelThumbnail(v1, conf)
|
||||||
|
|||||||
18
pkg/txt/clip.go
Normal file
18
pkg/txt/clip.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package txt
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
func Clip(s string, size int) string {
|
||||||
|
if s == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
runes := []rune(s)
|
||||||
|
|
||||||
|
if len(runes) > size {
|
||||||
|
s = string(runes[0:size-1])
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
19
pkg/txt/clip_test.go
Normal file
19
pkg/txt/clip_test.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package txt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestClip(t *testing.T) {
|
||||||
|
t.Run("clip", func(t *testing.T) {
|
||||||
|
assert.Equal(t, "I'm ä", Clip("I'm ä lazy BRoWN fox!", 6))
|
||||||
|
})
|
||||||
|
t.Run("ok", func(t *testing.T) {
|
||||||
|
assert.Equal(t, "I'm ä lazy BRoWN fox!", Clip("I'm ä lazy BRoWN fox!", 128))
|
||||||
|
})
|
||||||
|
t.Run("empty", func(t *testing.T) {
|
||||||
|
assert.Equal(t, "", Clip("", -1))
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package txt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestKeywords(t *testing.T) {
|
|
||||||
t.Run("cat", func(t *testing.T) {
|
|
||||||
keywords := Keywords("cat")
|
|
||||||
assert.Equal(t, []string{"cat"}, keywords)
|
|
||||||
})
|
|
||||||
t.Run("was", func(t *testing.T) {
|
|
||||||
keywords := Keywords("was")
|
|
||||||
assert.Equal(t, []string(nil), keywords)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
13
pkg/txt/slug.go
Normal file
13
pkg/txt/slug.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package txt
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// SlugToTitle converts a slug back to a title
|
||||||
|
func SlugToTitle(s string) string {
|
||||||
|
if s == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return Title(strings.Join(Words(s), " "))
|
||||||
|
}
|
||||||
|
|
||||||
@@ -7,11 +7,14 @@ import (
|
|||||||
|
|
||||||
var KeywordsRegexp = regexp.MustCompile("[\\p{L}]{3,}")
|
var KeywordsRegexp = regexp.MustCompile("[\\p{L}]{3,}")
|
||||||
|
|
||||||
// Keywords extracts keywords for indexing and returns them as string slice.
|
// Words returns a slice of words with at least 3 characters from a string.
|
||||||
func Keywords(s string) (results []string) {
|
func Words(s string) (results []string) {
|
||||||
all := KeywordsRegexp.FindAllString(s, -1)
|
return KeywordsRegexp.FindAllString(s, -1)
|
||||||
|
}
|
||||||
|
|
||||||
for _, w := range all {
|
// Keywords returns a slice of keywords without stopwords.
|
||||||
|
func Keywords(s string) (results []string) {
|
||||||
|
for _, w := range Words(s) {
|
||||||
w = strings.ToLower(w)
|
w = strings.ToLower(w)
|
||||||
|
|
||||||
if _, ok := Stopwords[w]; ok == false {
|
if _, ok := Stopwords[w]; ok == false {
|
||||||
29
pkg/txt/words_test.go
Normal file
29
pkg/txt/words_test.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package txt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWords(t *testing.T) {
|
||||||
|
t.Run("I'm a lazy brown fox!", func(t *testing.T) {
|
||||||
|
result := Words("I'm a lazy BRoWN fox!")
|
||||||
|
assert.Equal(t, []string{"lazy", "BRoWN", "fox"}, result)
|
||||||
|
})
|
||||||
|
t.Run("no result", func(t *testing.T) {
|
||||||
|
result := Words("x")
|
||||||
|
assert.Equal(t, []string(nil), result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKeywords(t *testing.T) {
|
||||||
|
t.Run("I'm a lazy brown fox!", func(t *testing.T) {
|
||||||
|
result := Keywords("I'm a lazy BRoWN img!")
|
||||||
|
assert.Equal(t, []string{"lazy", "brown"}, result)
|
||||||
|
})
|
||||||
|
t.Run("no result", func(t *testing.T) {
|
||||||
|
result := Keywords("was")
|
||||||
|
assert.Equal(t, []string(nil), result)
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user