Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes to tests and logs #73

Merged
merged 3 commits into from
Jun 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions api/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,11 @@ type (
Faces bool `envconfig:"SMRITI_ML_FACES" default:"true"`
PlacesProvider string `envconfig:"SMRITI_ML_PLACES_PROVIDER" default:"openstreetmap"`
ClassificationProvider string `envconfig:"SMRITI_ML_CLASSIFICATION_PROVIDER" default:"pytorch"`
ClassificationParams string `envconfig:"SMRITI_ML_CLASSIFICATION_PARAMS" default:"{\"file\":\"classification_v240508.pt\"}"`
ClassificationParams string `envconfig:"SMRITI_ML_CLASSIFICATION_PARAMS" default:"{\"file\":\"classification_v240615.pt\"}"`
OCRProvider string `envconfig:"SMRITI_ML_OCR_PROVIDER" default:"paddlepaddle"`
OCRParams string `envconfig:"SMRITI_ML_OCR_PARAMS" default:"{\"det_model_dir\":\"det_onnx\",\"rec_model_dir\":\"rec_onnx\",\"cls_model_dir\":\"cls_onnx\"}"`
SearchProvider string `envconfig:"SMRITI_ML_SEARCH_PROVIDER" default:"pytorch"`
SearchParams string `envconfig:"SMRITI_ML_SEARCH_PARAMS" default:"{\"tokenizer_dir\":\"search_tokenizer\",\"processor_dir\":\"search_processor\",\"text_file\":\"search_text_v240508.pt\",\"vision_file\":\"search_vision_v240508.pt\"}"` //nolint:lll
SearchParams string `envconfig:"SMRITI_ML_SEARCH_PARAMS" default:"{\"tokenizer_dir\":\"search_tokenizer\",\"processor_dir\":\"search_processor\",\"text_file\":\"search_text_v240615.pt\",\"vision_file\":\"search_vision_v240615.pt\"}"` //nolint:lll
FacesProvider string `envconfig:"SMRITI_ML_FACES_PROVIDER" default:"pytorch"`
FacesParams string `envconfig:"SMRITI_ML_FACES_PARAMS" default:"{\"minutes\":\"1\",\"face_threshold\":\"0.9\",\"model\":\"vggface2\",\"clustering\":\"annoy\"}"`
PreviewThumbnailParams string `envconfig:"SMRITI_ML_PREVIEW_THUMBNAIL_PARAMS" default:"{\"thumbnail_size\":\"512\"}"`
Expand Down
12 changes: 9 additions & 3 deletions api/internal/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"errors"
"time"

"github.com/go-redis/redis/v8"
"github.com/golang-jwt/jwt/v4"
uuid "github.com/satori/go.uuid"
"golang.org/x/exp/slog"
Expand Down Expand Up @@ -69,7 +70,9 @@ func RefreshTokens(cfg *config.Config, cache cache.Provider, refreshToken string
func RemoveTokens(cache cache.Provider, accessToken string) error {
refreshToken, err := cache.Get(accessToken)
if err != nil {
slog.Error("error getting access token from cache", "error", err)
if !errors.Is(err, redis.Nil) {
slog.Error("error getting access token from cache", "error", err)
}
return err
}

Expand All @@ -82,8 +85,11 @@ func RemoveTokens(cache cache.Provider, accessToken string) error {

// VerifyToken ...
func VerifyToken(cfg *config.Config, cache cache.Provider, accessToken string) (*TokenClaims, error) {
if _, err := cache.Get(accessToken); err != nil {
slog.Error("error getting access token from cache", "error", err)
_, err := cache.Get(accessToken)
if err != nil {
if !errors.Is(err, redis.Nil) {
slog.Error("error getting access token from cache", "error", err)
}
return nil, err
}

Expand Down
2 changes: 1 addition & 1 deletion api/internal/handlers/albums.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,10 @@ func (h *Handler) GetAlbum(ctx echo.Context) error {
Preload("CoverMediaItem").
First(&album)
if result.Error != nil {
slog.Error("error getting album", "error", result.Error)
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return echo.NewHTTPError(http.StatusNotFound, "album not found")
}
slog.Error("error getting album", "error", result.Error)
return echo.NewHTTPError(http.StatusInternalServerError, result.Error.Error())
}
return ctx.JSON(http.StatusOK, album)
Expand Down
2 changes: 1 addition & 1 deletion api/internal/handlers/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ func (h *Handler) Login(ctx echo.Context) error {
Where("username=? AND password=?", &loginRequest.Username, getPasswordHash(*loginRequest.Password)).
First(&user)
if result.Error != nil {
slog.Error("error getting user", "error", result.Error)
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return echo.NewHTTPError(http.StatusNotFound, "incorrect username or password")
}
slog.Error("error getting user", "error", result.Error)
return echo.NewHTTPError(http.StatusInternalServerError, result.Error.Error())
}
accessToken, refreshToken, err := auth.GetTokens(h.Config, h.Cache, user)
Expand Down
30 changes: 30 additions & 0 deletions api/internal/handlers/mediaitems_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1192,6 +1192,7 @@ func TestUploadMediaItems(t *testing.T) {
sampleFile4, contentType4 := getMockedMediaItemFile(t)
sampleFile5, contentType5 := getMockedMediaItemFile(t)
sampleFile6, contentType6 := getMockedMediaItemFile(t)
sampleFile7, contentType7 := getMockedMediaItemFile(t)
tests := []Test{
{
"upload mediaitems with invalid command",
Expand Down Expand Up @@ -1321,6 +1322,35 @@ func TestUploadMediaItems(t *testing.T) {
http.StatusInternalServerError,
"some db error",
},
{
"upload mediaitems with error saving hash due to duplicates",
http.MethodPost,
"/v1/mediaItems",
"/v1/mediaItems",
[]string{},
[]string{},
map[string]string{
echo.HeaderContentType: contentType7,
},
sampleFile7,
func(mock sqlmock.Sqlmock) {
mock.ExpectBegin()
mock.ExpectExec(regexp.QuoteMeta(`INSERT INTO "mediaitems"`)).
WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectCommit()
mock.ExpectBegin()
mock.ExpectExec(regexp.QuoteMeta(`UPDATE "mediaitems"`)).
WillReturnError(errors.New("violates unique constraint"))
mock.ExpectRollback()
},
nil,
nil,
func(handler *Handler) func(ctx echo.Context) error {
return handler.UploadMediaItems
},
http.StatusConflict,
"mediaitem already exists",
},
{
"upload mediaitems with error saving hash in mediaitem process",
http.MethodPost,
Expand Down
2 changes: 1 addition & 1 deletion api/internal/handlers/sharing.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ func (h *Handler) GetSharedAlbum(ctx echo.Context) error {
Preload("CoverMediaItem").
First(&sharedAlbum)
if result.Error != nil {
slog.Error("error getting shared album", "error", result.Error)
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return echo.NewHTTPError(http.StatusNotFound, "shared link not found")
}
slog.Error("error getting shared album", "error", result.Error)
return echo.NewHTTPError(http.StatusInternalServerError, result.Error.Error())
}
return ctx.JSON(http.StatusOK, sharedAlbum)
Expand Down
8 changes: 7 additions & 1 deletion api/internal/jobs/jobs.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"api/pkg/services/worker"
"api/pkg/storage"
"context"
"errors"
"fmt"
"log/slog"
"strconv"
Expand Down Expand Up @@ -110,6 +111,9 @@ func (j *Job) getJobMediaItem(jobCfg models.Job, lastMediaItemID uuid.UUID) (mod
mediaItem := models.MediaItem{}
result := j.DB.Where("user_id=? AND id>?", jobCfg.UserID, lastMediaItemID).Order("created_at").First(&mediaItem)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return mediaItem, result.Error
}
slog.Error("error getting job mediaitem", "error", result.Error)
return mediaItem, result.Error
}
Expand Down Expand Up @@ -246,7 +250,9 @@ func (j *Job) getPayload(jobCfg models.Job, mediaItem models.MediaItem) map[stri
payload["mimeType"] = mediaItem.MimeType
payload["type"] = string(mediaItem.MediaItemType)
payload["exifdata"] = *mediaItem.EXIFData
payload["keywords"] = *mediaItem.Keywords
if mediaItem.Keywords != nil {
payload["keywords"] = *mediaItem.Keywords
}
return payload
}

Expand Down
7 changes: 4 additions & 3 deletions api/pkg/storage/storage.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package storage

import (
"errors"
"os"

"github.com/minio/minio-go/v7"
Expand Down Expand Up @@ -51,15 +52,15 @@ func Init(cfg *Config) Provider { //nolint: ireturn
}
}
err := os.Mkdir(cfg.Root+"/originals", dirPermission)
if err != nil {
if err != nil && !errors.Is(err, os.ErrExist) {
slog.Error("error creating storage originals directory", "error", err)
}
err = os.Mkdir(cfg.Root+"/previews", dirPermission)
if err != nil {
if err != nil && !errors.Is(err, os.ErrExist) {
slog.Error("error creating storage previews directory", "error", err)
}
err = os.Mkdir(cfg.Root+"/thumbnails", dirPermission)
if err != nil {
if err != nil && !errors.Is(err, os.ErrExist) {
slog.Error("error creating storage thumbnails directory", "error", err)
}
return &Disk{Root: cfg.Root}
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/user-guide/features/search.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Enable this feature using API Configuration:
```bash
SMRITI_ML_SEARCH: true
SMRITI_ML_SEARCH_PROVIDER: pytorch
SMRITI_ML_SEARCH_PARAMS: {"tokenizer_dir":"search_tokenizer","processor_dir":"search_processor","text_file":"search_text_v240508.pt","vision_file":"search_vision_v240508.pt"}
SMRITI_ML_SEARCH_PARAMS: {"tokenizer_dir":"search_tokenizer","processor_dir":"search_processor","text_file":"search_text_v240615.pt","vision_file":"search_vision_v240615.pt"}
```

## Use Cases
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/user-guide/features/things.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Enable this feature using API Configuration:
SMRITI_FEATURE_THINGS: true
SMRITI_ML_CLASSIFICATION: true
SMRITI_ML_CLASSIFICATION_PROVIDER: pytorch
SMRITI_ML_CLASSIFICATION_PARAMS: {"file":"classification_v240508.pt"}
SMRITI_ML_CLASSIFICATION_PARAMS: {"file":"classification_v240615.pt"}
```

## Use Cases
Expand Down
1 change: 1 addition & 0 deletions ml/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
facenet-pytorch==2.5.3
numpy==1.26.4
opencv-python==4.10.0.84
Pillow==10.3.0
paddle2onnx==1.2.1
Expand Down
2 changes: 1 addition & 1 deletion tests/features/albums.feature
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Feature: Albums
Given a user default is created if does not exist
When user default logs in
Then token is generated
When upload default photo mediaitem with auth if does not exist and wait 10 seconds
When upload default photo mediaitem with auth if does not exist and wait 5 seconds
Then mediaitem is uploaded or exists

Scenario: Validate Create Album
Expand Down
1 change: 1 addition & 0 deletions tests/features/environment.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import psycopg2
import time


ALL_TABLES = ['thing_mediaitems', 'place_mediaitems',
Expand Down
2 changes: 1 addition & 1 deletion tests/features/jobs.feature
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Feature: Jobs
Then user jobs is created
When user jobs logs in
Then token is generated
When upload jobs.jpeg photo mediaitem with auth
When upload jobs.jpeg photo mediaitem with auth and wait 5 seconds
Then mediaitem is uploaded
When update jobs user with auth
Then user jobs is updated
Expand Down
2 changes: 1 addition & 1 deletion tests/features/library.feature
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Feature: Library
Given a user default is created if does not exist
When user default logs in
Then token is generated
When upload default photo mediaitem with auth if does not exist and wait 10 seconds
When upload default photo mediaitem with auth if does not exist and wait 5 seconds
Then mediaitem is uploaded or exists
When get mediaitem with auth
Then mediaitem is present
Expand Down
10 changes: 5 additions & 5 deletions tests/features/mediaitems.feature
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ Feature: MediaItems

Scenario: Validate Create Photo MediaItem
Given there are no mediaitems
When upload default photo mediaitem without auth
When upload default photo mediaitem without auth and wait 0 seconds
Then auth error is found
When upload default photo mediaitem with auth
When upload default photo mediaitem with auth and wait 5 seconds
Then mediaitem is uploaded
When get mediaitem without auth
Then auth error is found
Expand Down Expand Up @@ -37,7 +37,7 @@ Feature: MediaItems

Scenario: Validate Duplicate Photo MediaItem
Given a mediaitem exists
When upload default photo mediaitem with auth
When upload default photo mediaitem with auth and wait 0 seconds
Then mediaitem already exists

Scenario: Validate Delete Photo MediaItem
Expand All @@ -57,9 +57,9 @@ Feature: MediaItems

Scenario: Validate Create Video MediaItem
Given there are no mediaitems
When upload default video mediaitem without auth
When upload default video mediaitem without auth and wait 0 seconds
Then auth error is found
When upload default video mediaitem with auth
When upload default video mediaitem with auth and wait 30 seconds
Then mediaitem is uploaded
When get mediaitem without auth
Then auth error is found
Expand Down
2 changes: 1 addition & 1 deletion tests/features/places.feature
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Feature: Places
Given a user default is created if does not exist
When user default logs in
Then token is generated
When upload default photo mediaitem with auth if does not exist and wait 10 seconds
When upload default photo mediaitem with auth if does not exist and wait 5 seconds
Then mediaitem is uploaded or exists

Scenario: Validate Places
Expand Down
8 changes: 5 additions & 3 deletions tests/features/search.feature
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ Feature: Search
Given a user default is created if does not exist
When user default logs in
Then token is generated
When upload default photo mediaitem with auth if does not exist and wait 10 seconds
When upload default photo mediaitem with auth if does not exist and wait 5 seconds
Then mediaitem is uploaded or exists

Scenario: Search MediaItems
Given a mediaitem exists
When search for mediaitems without auth
When search query pizza for mediaitems without auth
Then auth error is found
When search for mediaitems with auth
When search query pizza for mediaitems with auth
Then searched mediaitem is present in list
When search query man playing guitar for mediaitems with auth
Then searched mediaitem is not present in list
2 changes: 1 addition & 1 deletion tests/features/sharing.feature
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Feature: Sharing
Given a user default is created if does not exist
When user default logs in
Then token is generated
When upload default photo mediaitem with auth if does not exist and wait 10 seconds
When upload default photo mediaitem with auth if does not exist and wait 5 seconds
Then mediaitem is uploaded or exists

Scenario: Validate Create Shared Album
Expand Down
5 changes: 3 additions & 2 deletions tests/features/steps/mediaitems.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,16 @@ def step_impl(context):
assert field not in context.mediaitem
assert context.mediaitem['message'] == 'mediaitem not found'

@when('upload {name} {type} mediaitem {condition} auth')
def step_impl(context, name, type, condition):
@when('upload {name} {type} mediaitem {condition} auth and wait {seconds} seconds')
def step_impl(context, name, type, condition, seconds):
headers = None
if condition == 'with':
headers = {'Authorization': f'Bearer {context.access_token}'}
files = {'file': open(f'data/{"IMG_0543.HEIC" if name == "default" and type == "photo" else "IMG_6470.MOV" if name == "default" and type =="video" else name}','rb')}
res = requests.post(API_URL+'/v1/mediaItems', files=files, headers=headers)
context.response = res
context.mediaitem_type = type
time.sleep(int(seconds))

@when('upload {name} {type} mediaitem with auth if does not exist and wait {seconds} seconds')
def step_impl(context, name, type, seconds):
Expand Down
11 changes: 8 additions & 3 deletions tests/features/steps/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,21 @@
from common import API_URL


@when('search for mediaitems {condition} auth')
def step_impl(context, condition):
@when('search query {query} for mediaitems {condition} auth')
def step_impl(context, query, condition):
headers = None
if condition == 'with':
headers = {'Authorization': f'Bearer {context.access_token}'}
res = requests.get(API_URL+'/v1/search?q=pizza', headers=headers)
res = requests.get(API_URL+'/v1/search?q='+query, headers=headers)
context.response = res
context.mediaitems = res.json()

@then('searched mediaitem is present in list')
def step_impl(context):
assert len(context.mediaitems) == 1
assert context.mediaitem_id == context.mediaitems[0]['id']

@then('searched mediaitem is not present in list')
def step_impl(context):
assert len(context.mediaitems) == 0
assert context.mediaitem_id not in [mediaitem['id'] for mediaitem in context.mediaitems]
2 changes: 1 addition & 1 deletion tests/features/things.feature
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Feature: Things
Given a user default is created if does not exist
When user default logs in
Then token is generated
When upload default photo mediaitem with auth if does not exist and wait 10 seconds
When upload default photo mediaitem with auth if does not exist and wait 5 seconds
Then mediaitem is uploaded or exists

Scenario: Validate Things
Expand Down
1 change: 1 addition & 0 deletions worker/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ grpcio==1.64.1
grpcio-tools==1.64.1
minio==7.2.7
moviepy==1.0.3
numpy==1.26.4
opencv-python==4.10.0.84
prometheus-client==0.20.0
Pillow==10.3.0
Expand Down
2 changes: 2 additions & 0 deletions worker/src/providers/faces/pytorch.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ def __init__(self, params: dict) -> None:
os.environ['TORCH_HOME'] = '/'
try:
os.symlink('/models/faces/', '/checkpoints')
except FileExistsError:
pass
except Exception as exp:
logging.error(f'error creating symlink: {str(exp)}')
self.prob_threshold = float(params['face_threshold'])
Expand Down
Loading
Loading