Skip to content

Commit 331b943

Browse files
longshuicytcnichol
andauthored
872 refactoring pagination (#894)
* might work * working now * model working now * everything in dataset should work * facet skip limit works * wire pagination to explore page * set model; but can't make id work * user has quirks but kinda works * files pagination * revert dataset.tsx change * file not working * need to replace pyObjectId with pydanticObjectId otherwise the objectId can't be json serialized * update to pydanticObjectId * clean up files component * file and datasets should all work now * toggle user works * fix everything with search user * fix group pagination * fix share group * file pagination should only stay in file page * apikey pagination works * metadata defintion refactored * listener jobs not quite working * listener pagination finished * rewrite sort * replace all pyObjectId to pydanticObjectId * extraction history hooked with backend * fix pytest * sorting works * black * black formatter * fix bug * more metadata bugs * update pages * moving to config * metadata plural to singualar * summarize page metadata into a function * use metadata construction function in dataset * upgrade black to latest version see if it fixes the linting issue * Revert "upgrade black to latest version see if it fixes the linting issue" This reverts commit aa660df. * pin black action version to 23.3.0 to match pipfile * black * 896 folder plus file pagination (#903) * fix folder pagination * fix metadata pagination bug * fix another one * flipping logic clean up * frontend pagination is impossible... * Revert "frontend pagination is impossible..." This reverts commit 41702ac. * the view is working * redux for folder and files * listing works * fix query bug * nested folder correct now * allow fancy sorting * fix subfolder and file pagination issue * remove outdated redux action and states * black * adjust file folder per page * 901 pagination update with public datasets (#907) * fix folder pagination * fix metadata pagination bug * fix another one * flipping logic clean up * frontend pagination is impossible... * Revert "frontend pagination is impossible..." This reverts commit 41702ac. * the view is working * redux for folder and files * listing works * fix query bug * nested folder correct now * allow fancy sorting * fix subfolder and file pagination issue * remove outdated redux action and states * black * public endpoint pagination conversion * public folder paths not working * fix public folder path * black * use utility function to construct page metadata * small fix, call to a method that did not exist for public datasets --------- Co-authored-by: toddn <[email protected]>
1 parent a9f1086 commit 331b943

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+2225
-2403
lines changed

.github/workflows/black.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,4 @@ jobs:
2121
runs-on: ubuntu-latest
2222
steps:
2323
- uses: actions/checkout@v2
24-
- uses: psf/black@stable
24+
- uses: psf/black@23.3.0

backend/app/deps/authorization_deps.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from app.models.files import FileOut, FileDB, FileStatus
99
from app.models.groups import GroupOut, GroupDB
1010
from app.models.metadata import MetadataDB
11-
from app.models.pyobjectid import PyObjectId
1211
from app.routers.authentication import get_admin
1312
from app.routers.authentication import get_admin_mode
1413

@@ -52,7 +51,7 @@ async def get_role(
5251
return RoleType.OWNER
5352

5453
authorization = await AuthorizationDB.find_one(
55-
AuthorizationDB.dataset_id == PyObjectId(dataset_id),
54+
AuthorizationDB.dataset_id == PydanticObjectId(dataset_id),
5655
Or(
5756
AuthorizationDB.creator == current_user,
5857
AuthorizationDB.user_ids == current_user,
@@ -207,7 +206,7 @@ async def __call__(
207206

208207
# Else check role assigned to the user
209208
authorization = await AuthorizationDB.find_one(
210-
AuthorizationDB.dataset_id == PyObjectId(dataset_id),
209+
AuthorizationDB.dataset_id == PydanticObjectId(dataset_id),
211210
Or(
212211
AuthorizationDB.creator == current_user,
213212
AuthorizationDB.user_ids == current_user,

backend/app/main.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from app.models.errors import ErrorDB
1616
from app.models.feeds import FeedDB
1717
from app.models.files import FileDB, FileVersionDB, FileDBViewList
18+
from app.models.folder_and_file import FolderFileViewList
1819
from app.models.folders import FolderDB, FolderDBViewList
1920
from app.models.groups import GroupDB
2021
from app.models.listeners import (
@@ -269,6 +270,7 @@ async def startup_beanie():
269270
VisualizationConfigDB,
270271
VisualizationDataDB,
271272
ThumbnailDB,
273+
FolderFileViewList,
272274
],
273275
recreate_views=True,
274276
)

backend/app/models/authorization.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
from datetime import datetime
22
from enum import Enum
33

4-
from beanie import Document
4+
from beanie import Document, PydanticObjectId
55
from charset_normalizer.md import List
66
from pydantic import BaseModel, EmailStr, Field
7-
from app.models.pyobjectid import PyObjectId
87

98

109
class RoleType(str, Enum):
@@ -34,10 +33,10 @@ class AuthorizationBase(BaseModel):
3433
group_ids are kept for convenience (adding/removing users in batch) but user_ids list MUST be kept current.
3534
"""
3635

37-
dataset_id: PyObjectId
36+
dataset_id: PydanticObjectId
3837
user_ids: List[EmailStr] = []
3938
role: RoleType
40-
group_ids: List[PyObjectId] = []
39+
group_ids: List[PydanticObjectId] = []
4140

4241
class Config:
4342
# required for Enum to properly work
@@ -59,18 +58,18 @@ class Config:
5958
class AuthorizationFile(BaseModel):
6059
# TODO: This should be PyObjectId = Field(default_factory=PyObjectId). Need to figure out why can't create instance
6160
# in `routers.authorization.get_dataset_role()`.
62-
file_id: PyObjectId
61+
file_id: PydanticObjectId
6362
user_ids: List[EmailStr] = []
6463
role: RoleType
65-
group_ids: List[PyObjectId] = []
64+
group_ids: List[PydanticObjectId] = []
6665

6766
class Config:
6867
# required for Enum to properly work
6968
use_enum_values = True
7069

7170

7271
class AuthorizationMetadata(BaseModel):
73-
metadata_id: PyObjectId
72+
metadata_id: PydanticObjectId
7473
user_id: EmailStr
7574
role: RoleType
7675

backend/app/models/files.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
from datetime import datetime
2-
from enum import Enum
3-
from typing import Optional, List
42
from enum import Enum, auto
3+
from typing import Optional, List
4+
55
from beanie import Document, View, PydanticObjectId
66
from pydantic import Field, BaseModel
77

88
from app.models.authorization import AuthorizationDB
9-
from app.models.pyobjectid import PyObjectId
109
from app.models.users import UserOut
1110

1211

@@ -78,15 +77,16 @@ class FileDB(Document, FileBase):
7877
created: datetime = Field(default_factory=datetime.utcnow)
7978
version_id: str = "N/A"
8079
version_num: int = 0
81-
dataset_id: PyObjectId
82-
folder_id: Optional[PyObjectId]
80+
dataset_id: PydanticObjectId
81+
folder_id: Optional[PydanticObjectId]
8382
views: int = 0
8483
downloads: int = 0
8584
bytes: int = 0
8685
content_type: ContentType = ContentType()
8786
thumbnail_id: Optional[PydanticObjectId] = None
8887
storage_type: StorageType = StorageType.MINIO
8988
storage_path: Optional[str] # store URL or file path depending on storage_type
89+
object_type: str = "file"
9090

9191
class Settings:
9292
name = "files"
@@ -100,8 +100,8 @@ class FileDBViewList(View, FileBase):
100100
id: PydanticObjectId = Field(None, alias="_id") # necessary for Views
101101
version_id: str = "N/A"
102102
version_num: int = 0
103-
dataset_id: PyObjectId
104-
folder_id: Optional[PyObjectId]
103+
dataset_id: PydanticObjectId
104+
folder_id: Optional[PydanticObjectId]
105105
creator: UserOut
106106
created: datetime = Field(default_factory=datetime.utcnow)
107107
modified: datetime = Field(default_factory=datetime.utcnow)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from datetime import datetime
2+
from typing import Optional, List
3+
4+
from beanie import View, PydanticObjectId
5+
from pydantic import Field
6+
7+
from app.models.authorization import AuthorizationDB
8+
from app.models.files import FileDB, FileBase, ContentType
9+
from app.models.users import UserOut
10+
11+
12+
class FolderFileViewList(View, FileBase):
13+
# common field
14+
object_type: str = Field(None, alias="object_type") # necessary for Views
15+
id: PydanticObjectId = Field(None, alias="_id") # necessary for Views
16+
dataset_id: PydanticObjectId
17+
creator: UserOut
18+
created: datetime = Field(default_factory=datetime.utcnow)
19+
modified: datetime = Field(default_factory=datetime.utcnow)
20+
name: str = "N/A"
21+
22+
# file
23+
version_id: str = "N/A"
24+
version_num: int = 0
25+
folder_id: Optional[PydanticObjectId]
26+
auth: List[AuthorizationDB]
27+
bytes: int = 0
28+
content_type: ContentType = ContentType()
29+
thumbnail_id: Optional[PydanticObjectId] = None
30+
31+
# folder
32+
parent_folder: Optional[PydanticObjectId]
33+
auth: List[AuthorizationDB]
34+
35+
class Settings:
36+
source = FileDB
37+
name = "folders_files_view"
38+
pipeline = [
39+
{
40+
"$lookup": {
41+
"from": "authorization",
42+
"localField": "dataset_id",
43+
"foreignField": "dataset_id",
44+
"as": "auth",
45+
}
46+
},
47+
{"$addFields": {"object_type": "file"}},
48+
{
49+
"$unionWith": {
50+
"coll": "folders",
51+
"pipeline": [{"$addFields": {"object_type": "folder"}}],
52+
}
53+
},
54+
]

backend/app/models/folders.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
from pydantic import Field, BaseModel
66

77
from app.models.authorization import AuthorizationDB
8-
from app.models.pyobjectid import PyObjectId
98
from app.models.users import UserOut
109

1110

@@ -14,24 +13,25 @@ class FolderBase(BaseModel):
1413

1514

1615
class FolderIn(FolderBase):
17-
parent_folder: Optional[PyObjectId]
16+
parent_folder: Optional[PydanticObjectId]
1817

1918

2019
class FolderDB(Document, FolderBase):
21-
dataset_id: PyObjectId
22-
parent_folder: Optional[PyObjectId]
20+
dataset_id: PydanticObjectId
21+
parent_folder: Optional[PydanticObjectId]
2322
creator: UserOut
2423
created: datetime = Field(default_factory=datetime.utcnow)
2524
modified: datetime = Field(default_factory=datetime.utcnow)
25+
object_type: str = "folder"
2626

2727
class Settings:
2828
name = "folders"
2929

3030

3131
class FolderDBViewList(View, FolderBase):
3232
id: PydanticObjectId = Field(None, alias="_id") # necessary for Views
33-
dataset_id: PyObjectId
34-
parent_folder: Optional[PyObjectId]
33+
dataset_id: PydanticObjectId
34+
parent_folder: Optional[PydanticObjectId]
3535
creator: UserOut
3636
created: datetime = Field(default_factory=datetime.utcnow)
3737
modified: datetime = Field(default_factory=datetime.utcnow)

backend/app/models/listeners.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
from app.config import settings
1010
from app.models.authorization import AuthorizationDB
1111
from app.models.mongomodel import MongoDBRef
12-
from app.models.pyobjectid import PyObjectId
1312
from app.models.users import UserOut
1413

1514

@@ -96,7 +95,7 @@ class FeedListener(BaseModel):
9695
"""This is a shorthand POST class for associating an existing EventListener with a Feed. The automatic flag determines
9796
whether the Feed will automatically send new matches to the Event Listener."""
9897

99-
listener_id: PyObjectId
98+
listener_id: PydanticObjectId
10099
automatic: bool # Listeners can trigger automatically or not on a per-feed basis.
101100

102101

backend/app/models/mongomodel.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
from typing import Optional
2-
from datetime import datetime
2+
3+
from beanie import PydanticObjectId
34
from bson import ObjectId
45
from bson.errors import InvalidId
5-
from pydantic import BaseModel, BaseConfig, Field
6-
from app.models.pyobjectid import PyObjectId
6+
from pydantic import BaseModel
77

88

99
class OID(str):
@@ -25,5 +25,5 @@ def __modify_schema__(cls, field_schema):
2525

2626
class MongoDBRef(BaseModel):
2727
collection: str
28-
resource_id: PyObjectId
28+
resource_id: PydanticObjectId
2929
version: Optional[int]

backend/app/models/pages.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
from beanie.odm.enums import SortDirection
2+
from pydantic import BaseModel
3+
4+
5+
class PageMetadata(BaseModel):
6+
total_count: int = 0
7+
skip: int = 0
8+
limit: int = 0
9+
10+
11+
class Paged(BaseModel):
12+
metadata: PageMetadata
13+
# order matters pydantic will match the first type that matches
14+
# data: Union[List[FileOut], List[DatasetOut], List[GroupOut], List[UserOut], List[FeedOut],
15+
# List[EventListenerJobOut], List[MetadataOut], List[MetadataDefinitionOut], List[EventListenerOut]]
16+
data: list = []
17+
18+
19+
def _get_page_query(
20+
skip, limit, sort_field="created", ascending=True, sort_clause=None
21+
):
22+
if sort_clause is not None:
23+
return {
24+
"$facet": {
25+
"metadata": [{"$count": "total_count"}],
26+
"data": [
27+
sort_clause,
28+
{"$skip": skip},
29+
{"$limit": limit},
30+
],
31+
},
32+
}
33+
else:
34+
return {
35+
"$facet": {
36+
"metadata": [{"$count": "total_count"}],
37+
"data": [
38+
{
39+
"$sort": {
40+
sort_field: SortDirection.ASCENDING
41+
if ascending
42+
else SortDirection.DESCENDING
43+
}
44+
},
45+
{"$skip": skip},
46+
{"$limit": limit},
47+
],
48+
},
49+
}
50+
51+
52+
def _construct_page_metadata(items_and_counts, skip, limit):
53+
if len(items_and_counts[0]["metadata"]) > 0:
54+
page_metadata = PageMetadata(
55+
**items_and_counts[0]["metadata"][0], skip=skip, limit=limit
56+
)
57+
else:
58+
page_metadata = PageMetadata(skip=skip, limit=limit)
59+
60+
return page_metadata

0 commit comments

Comments
 (0)