Remove pagination, refactoring
This commit is contained in:
@@ -2,9 +2,10 @@
|
||||
|
||||
## ToDo
|
||||
|
||||
1. Implement infinity scroll
|
||||
2. Implement S3 backend client
|
||||
3. Implement FTP backend client
|
||||
1. Implement cursor pagination on backends
|
||||
2. Implement infinity scroll
|
||||
3. Implement S3 backend client
|
||||
4. Implement FTP backend client
|
||||
|
||||
## User Flow
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ from soul_diary.backend.database.models import Sense, Session
|
||||
from .dependencies import is_auth, sense
|
||||
from .schemas import (
|
||||
CreateSenseRequest,
|
||||
Pagination,
|
||||
SenseListResponse,
|
||||
SenseResponse,
|
||||
UpdateSenseRequest,
|
||||
@@ -16,15 +15,9 @@ from .schemas import (
|
||||
async def get_sense_list(
|
||||
database: DatabaseService = fastapi.Depends(database),
|
||||
user_session: Session = fastapi.Depends(is_auth),
|
||||
pagination: Pagination = fastapi.Depends(Pagination),
|
||||
) -> SenseListResponse:
|
||||
async with database.transaction() as session:
|
||||
senses = await database.get_sense_list(
|
||||
session=session,
|
||||
user=user_session.user,
|
||||
page=pagination.page,
|
||||
limit=pagination.limit,
|
||||
)
|
||||
senses = await database.get_senses(session=session, user=user_session.user)
|
||||
|
||||
return SenseListResponse(data=senses)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import AwareDatetime, BaseModel, ConfigDict, PositiveInt
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
|
||||
class CreateSenseRequest(BaseModel):
|
||||
@@ -20,10 +20,5 @@ class SenseResponse(BaseModel):
|
||||
created_at: datetime
|
||||
|
||||
|
||||
class Pagination(BaseModel):
|
||||
page: PositiveInt = 1
|
||||
limit: PositiveInt = 10
|
||||
|
||||
|
||||
class SenseListResponse(BaseModel):
|
||||
data: list[SenseResponse]
|
||||
|
||||
@@ -99,18 +99,8 @@ class DatabaseService(ServiceMixin):
|
||||
|
||||
return user_session
|
||||
|
||||
async def get_sense_list(
|
||||
self,
|
||||
session: AsyncSession,
|
||||
user: User,
|
||||
page: int = 1,
|
||||
limit: int = 10,
|
||||
) -> list[Sense]:
|
||||
query = (
|
||||
select(Sense).where(Sense.user == user)
|
||||
.order_by(Sense.created_at.desc())
|
||||
.limit(limit).offset((page - 1) * limit)
|
||||
)
|
||||
async def get_senses(self, session: AsyncSession, user: User) -> list[Sense]:
|
||||
query = select(Sense).where(Sense.user == user).order_by(Sense.created_at.desc())
|
||||
|
||||
result = await session.execute(query)
|
||||
senses = result.scalars().all()
|
||||
|
||||
@@ -6,9 +6,9 @@ from typing import Any
|
||||
|
||||
from Cryptodome.Cipher import AES
|
||||
|
||||
from soul_diary.ui.app.backend.models import SenseBackendData
|
||||
from soul_diary.ui.app.local_storage import LocalStorage
|
||||
from soul_diary.ui.app.models import BackendType, Emotion, Options, Sense
|
||||
from soul_diary.ui.app.models import BackendType, Emotion, Sense
|
||||
from .models import EncryptedSense, EncryptedSenseList, SenseList, Options
|
||||
|
||||
|
||||
class BaseBackend:
|
||||
@@ -68,7 +68,7 @@ class BaseBackend:
|
||||
|
||||
return data_decoded
|
||||
|
||||
def convert_sense_data_to_sense(self, sense_data: SenseBackendData) -> Sense:
|
||||
def convert_encrypted_sense_to_sense(self, sense_data: EncryptedSense) -> Sense:
|
||||
return Sense(
|
||||
id=sense_data.id,
|
||||
created_at=sense_data.created_at,
|
||||
@@ -110,12 +110,13 @@ class BaseBackend:
|
||||
def is_auth(self) -> bool:
|
||||
return all((self._token, self._encryption_key))
|
||||
|
||||
async def get_sense_list(self, page: int = 1, limit: int = 10) -> list[Sense]:
|
||||
sense_data_list = await self.fetch_sense_list(page=page, limit=limit)
|
||||
return [
|
||||
self.convert_sense_data_to_sense(sense_data)
|
||||
for sense_data in sense_data_list
|
||||
async def get_sense_list(self) -> SenseList:
|
||||
encrypted_sense_list = await self.fetch_sense_list()
|
||||
senses = [
|
||||
self.convert_encrypted_sense_to_sense(encrypted_sense)
|
||||
for encrypted_sense in encrypted_sense_list.senses
|
||||
]
|
||||
return SenseList(senses=senses)
|
||||
|
||||
async def create_sense(
|
||||
self,
|
||||
@@ -132,13 +133,13 @@ class BaseBackend:
|
||||
}
|
||||
encoded_data = self.encode(data)
|
||||
|
||||
sense_data = await self.pull_sense_data(data=encoded_data)
|
||||
encrypted_sense = await self.pull_sense_data(data=encoded_data)
|
||||
|
||||
return self.convert_sense_data_to_sense(sense_data)
|
||||
return self.convert_encrypted_sense_to_sense(encrypted_sense)
|
||||
|
||||
async def get_sense(self, sense_id: uuid.UUID) -> Sense:
|
||||
sense_data = await self.fetch_sense(sense_id=sense_id)
|
||||
return self.convert_sense_data_to_sense(sense_data)
|
||||
encrypted_sense = await self.fetch_sense(sense_id=sense_id)
|
||||
return self.convert_encrypted_sense_to_sense(encrypted_sense)
|
||||
|
||||
async def edit_sense(
|
||||
self,
|
||||
@@ -156,9 +157,9 @@ class BaseBackend:
|
||||
}
|
||||
encoded_data = self.encode(data)
|
||||
|
||||
sense_data = await self.pull_sense_data(data=encoded_data, sense_id=sense_id)
|
||||
encrypted_sense = await self.pull_sense_data(data=encoded_data, sense_id=sense_id)
|
||||
|
||||
return self.convert_sense_data_to_sense(sense_data)
|
||||
return self.convert_encrypted_sense_to_sense(encrypted_sense)
|
||||
|
||||
def get_backend_data(self) -> dict[str, Any]:
|
||||
raise NotImplementedError
|
||||
@@ -175,21 +176,17 @@ class BaseBackend:
|
||||
async def get_options(self) -> Options:
|
||||
raise NotImplementedError
|
||||
|
||||
async def fetch_sense_list(
|
||||
self,
|
||||
page: int = 1,
|
||||
limit: int = 10,
|
||||
) -> list[SenseBackendData]:
|
||||
async def fetch_sense_list(self) -> EncryptedSenseList:
|
||||
raise NotImplementedError
|
||||
|
||||
async def fetch_sense(self, sense_id: uuid.UUID) -> SenseBackendData:
|
||||
async def fetch_sense(self, sense_id: uuid.UUID) -> EncryptedSense:
|
||||
raise NotImplementedError
|
||||
|
||||
async def pull_sense_data(
|
||||
self,
|
||||
data: str,
|
||||
sense_id: uuid.UUID | None = None,
|
||||
) -> SenseBackendData:
|
||||
) -> EncryptedSense:
|
||||
raise NotImplementedError
|
||||
|
||||
async def delete_sense(self, sense_id: uuid.UUID):
|
||||
|
||||
@@ -3,7 +3,7 @@ import uuid
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from soul_diary.ui.app.models import BackendType, Options
|
||||
from soul_diary.ui.app.models import BackendType
|
||||
from .base import BaseBackend
|
||||
from .exceptions import (
|
||||
IncorrectCredentialsException,
|
||||
@@ -11,7 +11,7 @@ from .exceptions import (
|
||||
SenseNotFoundException,
|
||||
UserAlreadyExistsException,
|
||||
)
|
||||
from .models import SenseBackendData
|
||||
from .models import EncryptedSense, EncryptedSenseList, Options
|
||||
|
||||
|
||||
class LocalBackend(BaseBackend):
|
||||
@@ -60,48 +60,39 @@ class LocalBackend(BaseBackend):
|
||||
async def get_options(self) -> Options:
|
||||
return Options(registration_enabled=True)
|
||||
|
||||
async def _fetch_sense_list(self) -> list[SenseBackendData]:
|
||||
async def fetch_sense_list(self) -> EncryptedSenseList:
|
||||
if not self.is_auth:
|
||||
raise NonAuthenticatedException()
|
||||
|
||||
sense_list_key = self.SENSE_LIST_KEY_TEMPLATE.format(username=self._username)
|
||||
sense_list = await self._local_storage.raw_read(sense_list_key) or []
|
||||
return [SenseBackendData.model_validate(sense) for sense in sense_list]
|
||||
senses = [EncryptedSense.model_validate(sense) for sense in sense_list]
|
||||
return EncryptedSenseList(senses=senses)
|
||||
|
||||
async def fetch_sense_list(
|
||||
self,
|
||||
page: int = 1,
|
||||
limit: int = 10,
|
||||
) -> list[SenseBackendData]:
|
||||
sense_list = await self._fetch_sense_list()
|
||||
sense_list_filtered = sense_list[(page - 1) * limit:page * limit]
|
||||
async def fetch_sense(self, sense_id: uuid.UUID) -> EncryptedSense:
|
||||
sense_list = await self.fetch_sense_list()
|
||||
|
||||
return sense_list_filtered
|
||||
|
||||
async def fetch_sense(self, sense_id: uuid.UUID) -> SenseBackendData:
|
||||
sense_list = await self._fetch_sense_list()
|
||||
|
||||
for sense in sense_list:
|
||||
for sense in sense_list.senses:
|
||||
if sense.id == sense_id:
|
||||
return sense
|
||||
|
||||
raise SenseNotFoundException()
|
||||
|
||||
async def pull_sense_data(self, data: str, sense_id: uuid.UUID | None = None) -> SenseBackendData:
|
||||
async def pull_sense_data(self, data: str, sense_id: uuid.UUID | None = None) -> EncryptedSense:
|
||||
sense_list_key = self.SENSE_LIST_KEY_TEMPLATE.format(username=self._username)
|
||||
sense_list = await self._fetch_sense_list()
|
||||
sense_list = await self.fetch_sense_list()
|
||||
|
||||
if sense_id is None:
|
||||
sense_ids = {sense.id for sense in sense_list}
|
||||
sense_ids = {sense.id for sense in sense_list.senses}
|
||||
sense_id = uuid.uuid4()
|
||||
while sense_id in sense_ids:
|
||||
sense_id = uuid.uuid4()
|
||||
sense = SenseBackendData(
|
||||
sense = EncryptedSense(
|
||||
id=sense_id,
|
||||
data=data,
|
||||
created_at=datetime.now().astimezone(),
|
||||
)
|
||||
sense_list.insert(0, sense)
|
||||
sense_list.senses.insert(0, sense)
|
||||
else:
|
||||
for index, sense in enumerate(sense_list):
|
||||
if sense.id == sense_id:
|
||||
@@ -109,20 +100,20 @@ class LocalBackend(BaseBackend):
|
||||
else:
|
||||
raise SenseNotFoundException()
|
||||
|
||||
sense = sense_list[index]
|
||||
sense = sense_list.senses[index]
|
||||
sense.data = data
|
||||
sense_list[index] = sense
|
||||
sense_list.senses[index] = sense
|
||||
|
||||
await self._local_storage.raw_write(
|
||||
sense_list_key,
|
||||
[sense.model_dump(mode="json") for sense in sense_list],
|
||||
[sense.model_dump(mode="json") for sense in sense_list.senses],
|
||||
)
|
||||
|
||||
return sense
|
||||
|
||||
async def delete_sense(self, sense_id: uuid.UUID):
|
||||
sense_list_key = self.SENSE_LIST_KEY_TEMPLATE.format(username=self._username)
|
||||
sense_list = await self._fetch_sense_list()
|
||||
sense_list = await self.fetch_sense_list()
|
||||
|
||||
for index, sense in enumerate(sense_list):
|
||||
if sense.id == sense_id:
|
||||
|
||||
@@ -3,8 +3,22 @@ from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from soul_diary.ui.app.models import Sense
|
||||
|
||||
class SenseBackendData(BaseModel):
|
||||
|
||||
class EncryptedSense(BaseModel):
|
||||
id: uuid.UUID
|
||||
data: str
|
||||
created_at: datetime
|
||||
|
||||
|
||||
class EncryptedSenseList(BaseModel):
|
||||
senses: list[EncryptedSense]
|
||||
|
||||
|
||||
class SenseList(BaseModel):
|
||||
senses: list[Sense]
|
||||
|
||||
|
||||
class Options(BaseModel):
|
||||
registration_enabled: bool
|
||||
|
||||
@@ -5,7 +5,7 @@ import httpx
|
||||
import yarl
|
||||
|
||||
from soul_diary.ui.app.local_storage import LocalStorage
|
||||
from soul_diary.ui.app.models import BackendType, Options
|
||||
from soul_diary.ui.app.models import BackendType
|
||||
from .base import BaseBackend
|
||||
from .exceptions import (
|
||||
IncorrectCredentialsException,
|
||||
@@ -14,7 +14,7 @@ from .exceptions import (
|
||||
SenseNotFoundException,
|
||||
UserAlreadyExistsException,
|
||||
)
|
||||
from .models import SenseBackendData
|
||||
from .models import EncryptedSense, EncryptedSenseList, Options
|
||||
|
||||
|
||||
class SoulBackend(BaseBackend):
|
||||
@@ -117,20 +117,15 @@ class SoulBackend(BaseBackend):
|
||||
|
||||
return Options.model_validate(response)
|
||||
|
||||
async def fetch_sense_list(
|
||||
self,
|
||||
page: int = 1,
|
||||
limit: int = 10,
|
||||
) -> list[SenseBackendData]:
|
||||
async def fetch_sense_list(self) -> EncryptedSenseList:
|
||||
path = "/senses/"
|
||||
params = {"page": page, "limit": limit}
|
||||
|
||||
response = await self.request(method="GET", path=path, params=params)
|
||||
senses = [SenseBackendData.model_validate(sense) for sense in response["data"]]
|
||||
response = await self.request(method="GET", path=path)
|
||||
senses = [EncryptedSense.model_validate(sense) for sense in response["data"]]
|
||||
|
||||
return senses
|
||||
return EncryptedSenseList(senses=senses)
|
||||
|
||||
async def fetch_sense(self, sense_id: uuid.UUID) -> SenseBackendData:
|
||||
async def fetch_sense(self, sense_id: uuid.UUID) -> EncryptedSense:
|
||||
path = f"/senses/{sense_id}"
|
||||
|
||||
try:
|
||||
@@ -141,13 +136,13 @@ class SoulBackend(BaseBackend):
|
||||
else:
|
||||
raise exc
|
||||
|
||||
return SenseBackendData.model_validate(response)
|
||||
return EncryptedSense.model_validate(response)
|
||||
|
||||
async def pull_sense_data(
|
||||
self,
|
||||
data: str,
|
||||
sense_id: uuid.UUID | None = None,
|
||||
) -> SenseBackendData:
|
||||
) -> EncryptedSense:
|
||||
path = "/senses/" if sense_id is None else f"/senses/{sense_id}"
|
||||
request_data = {"data": data}
|
||||
|
||||
@@ -159,7 +154,7 @@ class SoulBackend(BaseBackend):
|
||||
else:
|
||||
raise exc
|
||||
|
||||
return SenseBackendData.model_validate(response)
|
||||
return EncryptedSense.model_validate(response)
|
||||
|
||||
async def delete_sense(self, sense_id: uuid.UUID):
|
||||
path = f"/senses/{sense_id}"
|
||||
|
||||
@@ -26,7 +26,3 @@ class Sense(BaseModel):
|
||||
body: constr(min_length=1, strip_whitespace=True)
|
||||
desires: constr(min_length=1, strip_whitespace=True)
|
||||
created_at: datetime
|
||||
|
||||
|
||||
class Options(BaseModel):
|
||||
registration_enabled: bool
|
||||
|
||||
@@ -79,7 +79,7 @@ class DesiresPage(BasePage):
|
||||
body=body,
|
||||
).apply()
|
||||
|
||||
@callback_error_handle
|
||||
# @callback_error_handle
|
||||
async def callback_add_sense(self, event: flet.ControlEvent, desires_field: flet.TextField):
|
||||
if self.desires is None or not self.desires.strip():
|
||||
desires_field.error_text = "Коротко опиши свои желания"
|
||||
|
||||
@@ -65,12 +65,13 @@ class SenseListPage(BasePage):
|
||||
)
|
||||
|
||||
async def did_mount_async(self):
|
||||
backend_client = await get_backend_client(self.local_storage)
|
||||
sense_list = await backend_client.get_sense_list()
|
||||
self.senses = sense_list.senses
|
||||
await self.render_cards()
|
||||
|
||||
async def render_cards(self):
|
||||
function = self.render_extend_card if self.extend else self.render_compact_card
|
||||
backend_client = await get_backend_client(self.local_storage)
|
||||
self.senses = await backend_client.get_sense_list()
|
||||
self.senses_cards.controls = [await function(sense) for sense in self.senses]
|
||||
await self.update_async()
|
||||
|
||||
@@ -97,7 +98,7 @@ class SenseListPage(BasePage):
|
||||
content=flet.Card(
|
||||
content=flet.Container(
|
||||
content=flet.Column(controls=[feelings, bottom_row]),
|
||||
padding=10,
|
||||
padding=15,
|
||||
),
|
||||
width=600,
|
||||
height=150,
|
||||
@@ -150,7 +151,7 @@ class SenseListPage(BasePage):
|
||||
content=flet.Container(
|
||||
content=flet.Column(controls=[title, emotions, feelings_container, body_container,
|
||||
desires_container]),
|
||||
padding=10,
|
||||
padding=15,
|
||||
),
|
||||
width=600,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user