Skip to content

Commit 88dc168

Browse files
authored
683 include thumbnail ids in elasticsearch index (#756)
* adding method stub * adding index for thumbnails * index worked * thumbnail now has downloads field incremented False by default using that in es indexing * formatting * codegen
1 parent 78ac621 commit 88dc168

File tree

6 files changed

+76
-5
lines changed

6 files changed

+76
-5
lines changed

backend/app/models/thumbnails.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class ThumbnailDB(Document, ThumbnailBase):
2323
modified: datetime = Field(default_factory=datetime.utcnow)
2424
bytes: int = 0
2525
content_type: ContentType = ContentType()
26+
downloads: int = 0
2627

2728
class Settings:
2829
name = "thumbnails"

backend/app/routers/files.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
insert_record,
4343
update_record,
4444
)
45-
from app.search.index import index_file
45+
from app.search.index import index_file, index_thumbnail
4646

4747
router = APIRouter()
4848
security = HTTPBearer()
@@ -515,6 +515,7 @@ async def add_file_thumbnail(
515515
file_id: str,
516516
thumbnail_id: str,
517517
allow: bool = Depends(FileAuthorization("editor")),
518+
es: Elasticsearch = Depends(dependencies.get_elasticsearchclient),
518519
):
519520
if (file := await FileDB.get(PydanticObjectId(file_id))) is not None:
520521
if (
@@ -523,6 +524,9 @@ async def add_file_thumbnail(
523524
# TODO: Should we garbage collect existing thumbnail if nothing else points to it?
524525
file.thumbnail_id = thumbnail_id
525526
await file.save()
527+
await index_thumbnail(
528+
es, thumbnail_id, str(file.id), str(file.dataset_id), False
529+
)
526530
return file.dict()
527531
else:
528532
raise HTTPException(

backend/app/routers/thumbnails.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from typing import List, Optional
22

33
from beanie import PydanticObjectId
4+
from beanie.odm.operators.update.general import Inc
45
from fastapi import APIRouter, HTTPException, Depends
56
from fastapi import File, UploadFile
67
from fastapi.security import HTTPBearer
@@ -72,7 +73,9 @@ async def remove_thumbnail(thumb_id: str, fs: Minio = Depends(dependencies.get_f
7273

7374
@router.get("/{thumbnail_id}")
7475
async def download_thumbnail(
75-
thumbnail_id: str, fs: Minio = Depends(dependencies.get_fs)
76+
thumbnail_id: str,
77+
fs: Minio = Depends(dependencies.get_fs),
78+
increment: Optional[bool] = False,
7679
):
7780
# If thumbnail exists in MongoDB, download from Minio
7881
if (thumbnail := await ThumbnailDB.get(PydanticObjectId(thumbnail_id))) is not None:
@@ -81,6 +84,9 @@ async def download_thumbnail(
8184
# Get content type & open file stream
8285
response = StreamingResponse(content.stream(settings.MINIO_UPLOAD_CHUNK_SIZE))
8386
response.headers["Content-Disposition"] = "attachment; filename=%s" % "thumb"
87+
if increment:
88+
# Increment download count
89+
await thumbnail.update(Inc({ThumbnailDB.downloads: 1}))
8490
return response
8591
else:
8692
raise HTTPException(

backend/app/search/index.py

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33
from bson import ObjectId
44
from elasticsearch import Elasticsearch, NotFoundError
5-
5+
from beanie import PydanticObjectId
66
from app.config import settings
77
from app.models.authorization import AuthorizationDB
8-
from app.models.datasets import DatasetOut
9-
from app.models.files import FileOut
8+
from app.models.datasets import DatasetOut, DatasetDB
9+
from app.models.files import FileOut, FileDB
10+
from app.models.thumbnails import ThumbnailOut, ThumbnailDB
1011
from app.models.metadata import MetadataDB
1112
from app.models.search import (
1213
ElasticsearchEntry,
@@ -107,3 +108,56 @@ async def index_file(
107108
insert_record(es, settings.elasticsearch_index, doc, file.id)
108109
else:
109110
insert_record(es, settings.elasticsearch_index, doc, file.id)
111+
112+
113+
async def index_thumbnail(
114+
es: Elasticsearch,
115+
thumbnail_id: str,
116+
file_id: str,
117+
dataset_id: str,
118+
update: bool = False,
119+
):
120+
"""Create or update an Elasticsearch entry for the file. user_ids is the list of users
121+
with permission to at least view the file's dataset, it will be queried if not provided.
122+
"""
123+
if (file := await FileDB.get(PydanticObjectId(file_id))) is not None:
124+
if (
125+
thumbnail := await ThumbnailDB.get(PydanticObjectId(thumbnail_id))
126+
) is not None:
127+
# Get authorized users from db
128+
authorized_user_ids = []
129+
async for auth in AuthorizationDB.find(
130+
AuthorizationDB.dataset_id == ObjectId(dataset_id)
131+
):
132+
authorized_user_ids += auth.user_ids
133+
# Get full metadata from db (granular updates possible but complicated)
134+
metadata = []
135+
async for md in MetadataDB.find(
136+
MetadataDB.resource.resource_id == ObjectId(file.id)
137+
):
138+
metadata.append(md.content)
139+
# Add en entry to the file index
140+
doc = ElasticsearchEntry(
141+
resource_type="thumbnail",
142+
name=file.name,
143+
creator=thumbnail.creator.email,
144+
created=thumbnail.created,
145+
user_ids=authorized_user_ids,
146+
content_type=thumbnail.content_type.content_type,
147+
content_type_main=thumbnail.content_type.main_type,
148+
file_id=str(file.id),
149+
dataset_id=str(file.dataset_id),
150+
folder_id=str(file.folder_id),
151+
bytes=thumbnail.bytes,
152+
metadata=metadata,
153+
downloads=thumbnail.downloads,
154+
).dict()
155+
if update:
156+
try:
157+
update_record(
158+
es, settings.elasticsearch_index, {"doc": doc}, file.id
159+
)
160+
except NotFoundError:
161+
insert_record(es, settings.elasticsearch_index, doc, file.id)
162+
else:
163+
insert_record(es, settings.elasticsearch_index, doc, file.id)

frontend/src/openapi/v2/models/ThumbnailOut.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@ export type ThumbnailOut = {
2525
modified?: string;
2626
bytes?: number;
2727
content_type?: ContentType;
28+
downloads?: number;
2829
}

frontend/src/openapi/v2/services/ThumbnailsService.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,20 @@ export class ThumbnailsService {
3232
/**
3333
* Download Thumbnail
3434
* @param thumbnailId
35+
* @param increment
3536
* @returns any Successful Response
3637
* @throws ApiError
3738
*/
3839
public static downloadThumbnailApiV2ThumbnailsThumbnailIdGet(
3940
thumbnailId: string,
41+
increment: boolean = false,
4042
): CancelablePromise<any> {
4143
return __request({
4244
method: 'GET',
4345
path: `/api/v2/thumbnails/${thumbnailId}`,
46+
query: {
47+
'increment': increment,
48+
},
4449
errors: {
4550
422: `Validation Error`,
4651
},

0 commit comments

Comments
 (0)