feat: пагинация
This commit is contained in:
@@ -11,6 +11,7 @@ dependencies = [
|
||||
"asyncpg>=0.30.0",
|
||||
"beautifulsoup4>=4.14.2",
|
||||
"fastapi>=0.121.0",
|
||||
"fastapi-pagination>=0.15.3",
|
||||
"httpx>=0.28.1",
|
||||
"lxml>=6.0.2",
|
||||
"pydantic-settings>=2.11.0",
|
||||
|
||||
@@ -4,6 +4,7 @@ from typing import Annotated
|
||||
|
||||
from fastapi import Depends
|
||||
from fastapi.routing import APIRouter
|
||||
from fastapi_pagination import Page, paginate
|
||||
|
||||
from src import deps, dto
|
||||
from src.adapter.jwt import JWTPayload
|
||||
@@ -16,13 +17,14 @@ async def get_placements_analytics(
|
||||
workspace_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
project_id: uuid.UUID | None = None,
|
||||
) -> dto.GetPlacementsAnalyticsOutput:
|
||||
) -> Page[dto.PlacementAnalyticsOutput]:
|
||||
input = dto.GetPlacementsAnalyticsInput(
|
||||
user_id=current_user.user_id,
|
||||
workspace_id=workspace_id,
|
||||
project_id=project_id,
|
||||
)
|
||||
return await deps.get_usecase().get_placements_analytics(input)
|
||||
result = await deps.get_usecase().get_placements_analytics(input)
|
||||
return paginate(result) # type: ignore[no-any-return]
|
||||
|
||||
|
||||
@analytics_router.get('/creatives')
|
||||
@@ -30,13 +32,14 @@ async def get_creatives_analytics(
|
||||
workspace_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
project_id: uuid.UUID | None = None,
|
||||
) -> dto.GetCreativesAnalyticsOutput:
|
||||
) -> Page[dto.CreativeAnalyticsOutput]:
|
||||
input = dto.GetCreativesAnalyticsInput(
|
||||
user_id=current_user.user_id,
|
||||
workspace_id=workspace_id,
|
||||
project_id=project_id,
|
||||
)
|
||||
return await deps.get_usecase().get_creatives_analytics(input)
|
||||
result = await deps.get_usecase().get_creatives_analytics(input)
|
||||
return paginate(result) # type: ignore[no-any-return]
|
||||
|
||||
|
||||
@analytics_router.get('/channels')
|
||||
@@ -44,13 +47,14 @@ async def get_channel_analytics(
|
||||
workspace_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
project_id: uuid.UUID | None = None,
|
||||
) -> dto.GetChannelAnalyticsOutput:
|
||||
) -> Page[dto.ChannelAnalyticsOutput]:
|
||||
input = dto.GetChannelAnalyticsInput(
|
||||
user_id=current_user.user_id,
|
||||
workspace_id=workspace_id,
|
||||
project_id=project_id,
|
||||
)
|
||||
return await deps.get_usecase().get_channel_analytics(input)
|
||||
result = await deps.get_usecase().get_channel_analytics(input)
|
||||
return paginate(result) # type: ignore[no-any-return]
|
||||
|
||||
|
||||
@analytics_router.get('/spending')
|
||||
|
||||
@@ -2,6 +2,7 @@ from typing import Annotated
|
||||
|
||||
from fastapi import Depends
|
||||
from fastapi.routing import APIRouter
|
||||
from fastapi_pagination import Page, paginate
|
||||
|
||||
from src import deps, dto
|
||||
from src.adapter.jwt import JWTPayload
|
||||
@@ -13,6 +14,7 @@ channels_router = APIRouter(prefix='/channels', tags=['channels'])
|
||||
async def get_channels(
|
||||
_: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
username: str | None = None,
|
||||
) -> dto.GetChannelsOutput:
|
||||
) -> Page[dto.ChannelOutput]:
|
||||
input = dto.GetChannelsInput(username=username)
|
||||
return await deps.get_usecase().get_channels(input=input)
|
||||
result = await deps.get_usecase().get_channels(input=input)
|
||||
return paginate(result) # type: ignore[no-any-return]
|
||||
|
||||
@@ -3,6 +3,7 @@ from typing import Annotated
|
||||
|
||||
from fastapi import Depends, Query
|
||||
from fastapi.routing import APIRouter
|
||||
from fastapi_pagination import Page, paginate
|
||||
|
||||
from src import deps, dto
|
||||
from src.adapter.jwt import JWTPayload
|
||||
@@ -16,7 +17,7 @@ async def list_creatives(
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
project_id: uuid.UUID | None = None,
|
||||
include_archived: bool = False,
|
||||
) -> dto.GetCreativesOutput:
|
||||
) -> Page[dto.CreativeOutput]:
|
||||
input = dto.GetCreativesInput(
|
||||
user_id=current_user.user_id,
|
||||
workspace_id=workspace_id,
|
||||
@@ -24,7 +25,8 @@ async def list_creatives(
|
||||
include_archived=include_archived,
|
||||
)
|
||||
|
||||
return await deps.get_usecase().get_creatives(input=input)
|
||||
result = await deps.get_usecase().get_creatives(input=input)
|
||||
return paginate(result) # type: ignore[no-any-return]
|
||||
|
||||
|
||||
@creatives_router.get('/{creative_id}')
|
||||
|
||||
@@ -3,6 +3,7 @@ from typing import Annotated
|
||||
|
||||
from fastapi import Depends
|
||||
from fastapi.routing import APIRouter
|
||||
from fastapi_pagination import Page, paginate
|
||||
|
||||
from src import deps, dto
|
||||
from src.adapter.jwt import JWTPayload
|
||||
@@ -18,7 +19,7 @@ async def list_placements(
|
||||
placement_channel_id: uuid.UUID | None = None,
|
||||
creative_id: uuid.UUID | None = None,
|
||||
include_archived: bool = False,
|
||||
) -> dto.GetPlacementsOutput:
|
||||
) -> Page[dto.PlacementOutput]:
|
||||
input = dto.GetPlacementsInput(
|
||||
user_id=current_user.user_id,
|
||||
workspace_id=workspace_id,
|
||||
@@ -28,7 +29,8 @@ async def list_placements(
|
||||
include_archived=include_archived,
|
||||
)
|
||||
|
||||
return await deps.get_usecase().get_placements(input=input)
|
||||
result = await deps.get_usecase().get_placements(input=input)
|
||||
return paginate(result) # type: ignore[no-any-return]
|
||||
|
||||
|
||||
@placements_router.get('/{placement_id}')
|
||||
|
||||
@@ -3,6 +3,7 @@ from typing import Annotated
|
||||
|
||||
from fastapi import Depends
|
||||
from fastapi.routing import APIRouter
|
||||
from fastapi_pagination import Page, paginate
|
||||
|
||||
from src import deps, dto
|
||||
from src.adapter.jwt import JWTPayload
|
||||
@@ -14,10 +15,11 @@ projects_router = APIRouter(prefix='/workspaces/{workspace_id}/projects', tags=[
|
||||
async def get_workspace_projects(
|
||||
workspace_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
) -> dto.GetWorkspaceProjectsOutput:
|
||||
) -> Page[dto.ProjectOutput]:
|
||||
input = dto.GetWorkspaceProjectsInput(
|
||||
user_id=current_user.user_id,
|
||||
workspace_id=workspace_id,
|
||||
)
|
||||
|
||||
return await deps.get_usecase().get_workspace_projects(input=input)
|
||||
result = await deps.get_usecase().get_workspace_projects(input=input)
|
||||
return paginate(result) # type: ignore[no-any-return]
|
||||
|
||||
@@ -3,6 +3,7 @@ from typing import Annotated
|
||||
|
||||
from fastapi import Depends
|
||||
from fastapi.routing import APIRouter
|
||||
from fastapi_pagination import Page, paginate
|
||||
|
||||
from src import deps, dto
|
||||
from src.adapter.jwt import JWTPayload
|
||||
@@ -18,13 +19,14 @@ async def get_purchase_plan_channels(
|
||||
workspace_id: uuid.UUID,
|
||||
project_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
) -> dto.GetPurchasePlanChannelsOutput:
|
||||
) -> Page[dto.PurchasePlanChannelOutput]:
|
||||
input = dto.GetPurchasePlanChannelsInput(
|
||||
user_id=current_user.user_id,
|
||||
workspace_id=workspace_id,
|
||||
project_id=project_id,
|
||||
)
|
||||
return await deps.get_usecase().get_purchase_plan_channels(input=input)
|
||||
result = await deps.get_usecase().get_purchase_plan_channels(input=input)
|
||||
return paginate(result) # type: ignore[no-any-return]
|
||||
|
||||
|
||||
@purchase_plan_channels_router.post('')
|
||||
|
||||
@@ -4,6 +4,7 @@ from typing import Annotated
|
||||
|
||||
from fastapi import Depends
|
||||
from fastapi.routing import APIRouter
|
||||
from fastapi_pagination import Page, paginate
|
||||
|
||||
from src import deps, dto
|
||||
from src.adapter.jwt import JWTPayload
|
||||
@@ -18,7 +19,7 @@ async def get_views_history(
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
from_date: datetime.datetime | None = None,
|
||||
to_date: datetime.datetime | None = None,
|
||||
) -> dto.GetViewsHistoryOutput:
|
||||
) -> Page[dto.PostViewsHistoryOutput]:
|
||||
input_data = dto.GetViewsHistoryInput(
|
||||
placement_id=placement_id,
|
||||
user_id=current_user.user_id,
|
||||
@@ -27,4 +28,5 @@ async def get_views_history(
|
||||
to_date=to_date,
|
||||
)
|
||||
|
||||
return await deps.get_usecase().get_views_history(input=input_data)
|
||||
result = await deps.get_usecase().get_views_history(input=input_data)
|
||||
return paginate(result) # type: ignore[no-any-return]
|
||||
|
||||
@@ -3,6 +3,7 @@ from typing import Annotated
|
||||
|
||||
from fastapi import Depends
|
||||
from fastapi.routing import APIRouter
|
||||
from fastapi_pagination import Page, paginate
|
||||
|
||||
from src import deps, dto
|
||||
from src.adapter.jwt import JWTPayload
|
||||
@@ -14,11 +15,12 @@ workspace_invites_router = APIRouter(prefix='/workspaces/{workspace_id}/invites'
|
||||
async def list_workspace_invites(
|
||||
workspace_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
) -> dto.GetWorkspaceInvitesOutput:
|
||||
return await deps.get_usecase().get_workspace_invites(
|
||||
) -> Page[dto.WorkspaceInviteOutput]:
|
||||
result = await deps.get_usecase().get_workspace_invites(
|
||||
workspace_id=workspace_id,
|
||||
user_id=current_user.user_id,
|
||||
)
|
||||
return paginate(result) # type: ignore[no-any-return]
|
||||
|
||||
|
||||
@workspace_invites_router.post('')
|
||||
|
||||
@@ -3,6 +3,7 @@ from typing import Annotated
|
||||
|
||||
from fastapi import Depends
|
||||
from fastapi.routing import APIRouter
|
||||
from fastapi_pagination import Page, paginate
|
||||
|
||||
from src import deps, dto
|
||||
from src.adapter.jwt import JWTPayload
|
||||
@@ -14,11 +15,12 @@ workspace_members_router = APIRouter(prefix='/workspaces/{workspace_id}/members'
|
||||
async def list_workspace_members(
|
||||
workspace_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
) -> dto.GetWorkspaceMembersOutput:
|
||||
return await deps.get_usecase().get_workspace_members(
|
||||
) -> Page[dto.WorkspaceMemberOutput]:
|
||||
result = await deps.get_usecase().get_workspace_members(
|
||||
workspace_id=workspace_id,
|
||||
user_id=current_user.user_id,
|
||||
)
|
||||
return paginate(result) # type: ignore[no-any-return]
|
||||
|
||||
|
||||
@workspace_members_router.put('/{workspace_user_id}/permissions')
|
||||
|
||||
@@ -3,6 +3,7 @@ from typing import Annotated
|
||||
|
||||
from fastapi import Depends
|
||||
from fastapi.routing import APIRouter
|
||||
from fastapi_pagination import Page, paginate
|
||||
|
||||
from src import deps, dto
|
||||
from src.adapter.jwt import JWTPayload
|
||||
@@ -13,8 +14,9 @@ workspaces_router = APIRouter(prefix='/workspaces', tags=['workspaces'])
|
||||
@workspaces_router.get('')
|
||||
async def list_workspaces(
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
) -> dto.GetWorkspacesOutput:
|
||||
return await deps.get_usecase().get_workspaces(current_user.user_id)
|
||||
) -> Page[dto.WorkspaceMembershipOutput]:
|
||||
result = await deps.get_usecase().get_workspaces(current_user.user_id)
|
||||
return paginate(result) # type: ignore[no-any-return]
|
||||
|
||||
|
||||
@workspaces_router.post('')
|
||||
|
||||
@@ -3,6 +3,7 @@ from contextlib import asynccontextmanager
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi_pagination import add_pagination
|
||||
|
||||
from shared import logger
|
||||
from shared.worker_base import WorkerConfig
|
||||
@@ -68,4 +69,6 @@ app.add_middleware(
|
||||
allow_headers=['*'],
|
||||
)
|
||||
|
||||
add_pagination(app)
|
||||
|
||||
app.include_router(api_router)
|
||||
|
||||
@@ -11,7 +11,9 @@ if TYPE_CHECKING:
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def get_channel_analytics(self: 'Usecase', input: dto.GetChannelAnalyticsInput) -> dto.GetChannelAnalyticsOutput:
|
||||
async def get_channel_analytics(
|
||||
self: 'Usecase', input: dto.GetChannelAnalyticsInput
|
||||
) -> list[dto.ChannelAnalyticsOutput]:
|
||||
context = await self.ensure_workspace_permission(
|
||||
input.workspace_id, input.user_id, domain.PermissionKey.ANALYTICS_READ
|
||||
)
|
||||
@@ -105,4 +107,4 @@ async def get_channel_analytics(self: 'Usecase', input: dto.GetChannelAnalyticsI
|
||||
)
|
||||
)
|
||||
|
||||
return dto.GetChannelAnalyticsOutput(channels=result)
|
||||
return result
|
||||
|
||||
@@ -13,7 +13,7 @@ log = logging.getLogger(__name__)
|
||||
|
||||
async def get_creatives_analytics(
|
||||
self: 'Usecase', input: dto.GetCreativesAnalyticsInput
|
||||
) -> dto.GetCreativesAnalyticsOutput:
|
||||
) -> list[dto.CreativeAnalyticsOutput]:
|
||||
context = await self.ensure_workspace_permission(
|
||||
input.workspace_id, input.user_id, domain.PermissionKey.ANALYTICS_READ
|
||||
)
|
||||
@@ -94,4 +94,4 @@ async def get_creatives_analytics(
|
||||
)
|
||||
)
|
||||
|
||||
return dto.GetCreativesAnalyticsOutput(creatives=result)
|
||||
return result
|
||||
|
||||
@@ -23,7 +23,7 @@ def _calculate_cpm(cost: float | None, views: int | None) -> float | None:
|
||||
|
||||
async def get_placements_analytics(
|
||||
self: 'Usecase', input: dto.GetPlacementsAnalyticsInput
|
||||
) -> dto.GetPlacementsAnalyticsOutput:
|
||||
) -> list[dto.PlacementAnalyticsOutput]:
|
||||
context = await self.ensure_workspace_permission(
|
||||
input.workspace_id, input.user_id, domain.PermissionKey.ANALYTICS_READ
|
||||
)
|
||||
@@ -48,7 +48,7 @@ async def get_placements_analytics(
|
||||
post_ids = [p.post.id for p in placements if p.post]
|
||||
views_map = await self.database.get_latest_views_data_batch(post_ids) if post_ids else {}
|
||||
|
||||
result = [
|
||||
return [
|
||||
dto.PlacementAnalyticsOutput(
|
||||
id=p.id,
|
||||
project_id=p.project_id,
|
||||
@@ -66,5 +66,3 @@ async def get_placements_analytics(
|
||||
)
|
||||
for p in placements
|
||||
]
|
||||
|
||||
return dto.GetPlacementsAnalyticsOutput(placements=result)
|
||||
|
||||
@@ -9,19 +9,17 @@ if TYPE_CHECKING:
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def get_channels(self: 'Usecase', input: dto.GetChannelsInput) -> dto.GetChannelsOutput:
|
||||
async def get_channels(self: 'Usecase', input: dto.GetChannelsInput) -> list[dto.ChannelOutput]:
|
||||
channels = await self.database.search_channels(username_query=input.username)
|
||||
|
||||
log.debug('Found %s channels for username query: %s', len(channels), input.username)
|
||||
|
||||
return dto.GetChannelsOutput(
|
||||
channels=[
|
||||
dto.ChannelOutput(
|
||||
id=channel.id,
|
||||
telegram_id=channel.telegram_id,
|
||||
title=channel.title,
|
||||
username=channel.username,
|
||||
)
|
||||
for channel in channels
|
||||
]
|
||||
)
|
||||
return [
|
||||
dto.ChannelOutput(
|
||||
id=channel.id,
|
||||
telegram_id=channel.telegram_id,
|
||||
title=channel.title,
|
||||
username=channel.username,
|
||||
)
|
||||
for channel in channels
|
||||
]
|
||||
|
||||
@@ -6,7 +6,7 @@ if TYPE_CHECKING:
|
||||
from .. import Usecase
|
||||
|
||||
|
||||
async def get_creatives(self: 'Usecase', input: dto.GetCreativesInput) -> dto.GetCreativesOutput:
|
||||
async def get_creatives(self: 'Usecase', input: dto.GetCreativesInput) -> list[dto.CreativeOutput]:
|
||||
context = await self.ensure_workspace_permission(
|
||||
input.workspace_id, input.user_id, domain.PermissionKey.PROJECTS_READ
|
||||
)
|
||||
@@ -24,18 +24,16 @@ async def get_creatives(self: 'Usecase', input: dto.GetCreativesInput) -> dto.Ge
|
||||
allowed_project_ids=allowed_project_ids,
|
||||
)
|
||||
|
||||
return dto.GetCreativesOutput(
|
||||
creatives=[
|
||||
dto.CreativeOutput(
|
||||
id=creative.id,
|
||||
name=creative.name,
|
||||
text=creative.text,
|
||||
project_id=creative.project_id,
|
||||
project_channel_title=creative.project.channel.title,
|
||||
created_at=creative.created_at,
|
||||
status=creative.status,
|
||||
placements_count=creative.placements_count,
|
||||
)
|
||||
for creative in creatives
|
||||
]
|
||||
)
|
||||
return [
|
||||
dto.CreativeOutput(
|
||||
id=creative.id,
|
||||
name=creative.name,
|
||||
text=creative.text,
|
||||
project_id=creative.project_id,
|
||||
project_channel_title=creative.project.channel.title,
|
||||
created_at=creative.created_at,
|
||||
status=creative.status,
|
||||
placements_count=creative.placements_count,
|
||||
)
|
||||
for creative in creatives
|
||||
]
|
||||
|
||||
@@ -8,7 +8,7 @@ if TYPE_CHECKING:
|
||||
from .. import Usecase
|
||||
|
||||
|
||||
async def get_placements(self: 'Usecase', input: dto.GetPlacementsInput) -> dto.GetPlacementsOutput:
|
||||
async def get_placements(self: 'Usecase', input: dto.GetPlacementsInput) -> list[dto.PlacementOutput]:
|
||||
context = await self.ensure_workspace_permission(
|
||||
input.workspace_id, input.user_id, domain.PermissionKey.PLACEMENTS_READ
|
||||
)
|
||||
@@ -56,4 +56,4 @@ async def get_placements(self: 'Usecase', input: dto.GetPlacementsInput) -> dto.
|
||||
)
|
||||
)
|
||||
|
||||
return dto.GetPlacementsOutput(placements=placement_outputs)
|
||||
return placement_outputs
|
||||
|
||||
@@ -8,7 +8,7 @@ if TYPE_CHECKING:
|
||||
|
||||
async def get_workspace_projects(
|
||||
self: 'Usecase', input: dto.GetWorkspaceProjectsInput
|
||||
) -> dto.GetWorkspaceProjectsOutput:
|
||||
) -> list[dto.ProjectOutput]:
|
||||
context = await self.ensure_workspace_permission(
|
||||
input.workspace_id, input.user_id, domain.PermissionKey.PROJECTS_READ
|
||||
)
|
||||
@@ -17,15 +17,13 @@ async def get_workspace_projects(
|
||||
|
||||
projects = await self.database.get_workspace_projects(input.workspace_id, allowed_project_ids=allowed_project_ids)
|
||||
|
||||
return dto.GetWorkspaceProjectsOutput(
|
||||
projects=[
|
||||
dto.ProjectOutput(
|
||||
id=project.id,
|
||||
telegram_id=project.channel.telegram_id,
|
||||
title=project.channel.title,
|
||||
username=project.channel.username,
|
||||
status=project.status,
|
||||
)
|
||||
for project in projects
|
||||
]
|
||||
)
|
||||
return [
|
||||
dto.ProjectOutput(
|
||||
id=project.id,
|
||||
telegram_id=project.channel.telegram_id,
|
||||
title=project.channel.title,
|
||||
username=project.channel.username,
|
||||
status=project.status,
|
||||
)
|
||||
for project in projects
|
||||
]
|
||||
|
||||
@@ -11,7 +11,7 @@ log = logging.getLogger(__name__)
|
||||
|
||||
async def get_purchase_plan_channels(
|
||||
self: 'Usecase', input: dto.GetPurchasePlanChannelsInput
|
||||
) -> dto.GetPurchasePlanChannelsOutput:
|
||||
) -> list[dto.PurchasePlanChannelOutput]:
|
||||
context = await self.ensure_workspace_permission(
|
||||
input.workspace_id, input.user_id, domain.PermissionKey.PLACEMENTS_READ
|
||||
)
|
||||
@@ -27,20 +27,18 @@ async def get_purchase_plan_channels(
|
||||
plan_channels = await self.database.get_purchase_plan_channels(plan.id)
|
||||
log.debug('Fetched %s channels for purchase plan %s (project %s)', len(plan_channels), plan.id, project.id)
|
||||
|
||||
return dto.GetPurchasePlanChannelsOutput(
|
||||
channels=[
|
||||
dto.PurchasePlanChannelOutput(
|
||||
id=plan_channel.id,
|
||||
status=plan_channel.status,
|
||||
planned_cost=plan_channel.planned_cost,
|
||||
comment=plan_channel.comment,
|
||||
channel=dto.ChannelOutput(
|
||||
id=plan_channel.channel.id,
|
||||
telegram_id=plan_channel.channel.telegram_id,
|
||||
title=plan_channel.channel.title,
|
||||
username=plan_channel.channel.username,
|
||||
),
|
||||
)
|
||||
for plan_channel in plan_channels
|
||||
]
|
||||
)
|
||||
return [
|
||||
dto.PurchasePlanChannelOutput(
|
||||
id=plan_channel.id,
|
||||
status=plan_channel.status,
|
||||
planned_cost=plan_channel.planned_cost,
|
||||
comment=plan_channel.comment,
|
||||
channel=dto.ChannelOutput(
|
||||
id=plan_channel.channel.id,
|
||||
telegram_id=plan_channel.channel.telegram_id,
|
||||
title=plan_channel.channel.title,
|
||||
username=plan_channel.channel.username,
|
||||
),
|
||||
)
|
||||
for plan_channel in plan_channels
|
||||
]
|
||||
|
||||
@@ -9,7 +9,7 @@ if TYPE_CHECKING:
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def get_views_history(self: 'Usecase', input: dto.GetViewsHistoryInput) -> dto.GetViewsHistoryOutput:
|
||||
async def get_views_history(self: 'Usecase', input: dto.GetViewsHistoryInput) -> list[dto.PostViewsHistoryOutput]:
|
||||
context = await self.ensure_workspace_permission(
|
||||
input.workspace_id, input.user_id, domain.PermissionKey.PLACEMENTS_READ
|
||||
)
|
||||
@@ -22,7 +22,7 @@ async def get_views_history(self: 'Usecase', input: dto.GetViewsHistoryInput) ->
|
||||
context.ensure_project_permission(domain.PermissionKey.PLACEMENTS_READ, placement.project_id)
|
||||
|
||||
if not placement.post:
|
||||
return dto.GetViewsHistoryOutput(histories=[])
|
||||
return []
|
||||
|
||||
histories = await self.database.get_views_history(
|
||||
placement.post.id,
|
||||
@@ -30,15 +30,13 @@ async def get_views_history(self: 'Usecase', input: dto.GetViewsHistoryInput) ->
|
||||
to_date=input.to_date,
|
||||
)
|
||||
|
||||
return dto.GetViewsHistoryOutput(
|
||||
histories=[
|
||||
dto.PostViewsHistoryOutput(
|
||||
id=history.id,
|
||||
post_id=history.post_id,
|
||||
views_count=history.views_count,
|
||||
fetched_at=history.fetched_at,
|
||||
created_at=history.created_at,
|
||||
)
|
||||
for history in histories
|
||||
]
|
||||
)
|
||||
return [
|
||||
dto.PostViewsHistoryOutput(
|
||||
id=history.id,
|
||||
post_id=history.post_id,
|
||||
views_count=history.views_count,
|
||||
fetched_at=history.fetched_at,
|
||||
created_at=history.created_at,
|
||||
)
|
||||
for history in histories
|
||||
]
|
||||
|
||||
@@ -12,9 +12,9 @@ if TYPE_CHECKING:
|
||||
|
||||
async def get_workspace_invites(
|
||||
self: Usecase, workspace_id: uuid.UUID, user_id: uuid.UUID
|
||||
) -> dto.GetWorkspaceInvitesOutput:
|
||||
) -> list[dto.WorkspaceInviteOutput]:
|
||||
await self.ensure_workspace_permission(workspace_id, user_id, domain.PermissionKey.ADMIN_FULL)
|
||||
|
||||
invites = await self.database.get_workspace_invites(workspace_id)
|
||||
|
||||
return dto.GetWorkspaceInvitesOutput(invites=[workspace_invite_to_dto(invite) for invite in invites])
|
||||
return [workspace_invite_to_dto(invite) for invite in invites]
|
||||
|
||||
@@ -12,9 +12,9 @@ if TYPE_CHECKING:
|
||||
|
||||
async def get_workspace_members(
|
||||
self: Usecase, workspace_id: uuid.UUID, user_id: uuid.UUID
|
||||
) -> dto.GetWorkspaceMembersOutput:
|
||||
) -> list[dto.WorkspaceMemberOutput]:
|
||||
await self.ensure_workspace_permission(workspace_id, user_id, domain.PermissionKey.ADMIN_FULL)
|
||||
|
||||
members = await self.database.get_workspace_members(workspace_id)
|
||||
|
||||
return dto.GetWorkspaceMembersOutput(members=[workspace_member_to_dto(member) for member in members])
|
||||
return [workspace_member_to_dto(member) for member in members]
|
||||
|
||||
@@ -7,15 +7,13 @@ if TYPE_CHECKING:
|
||||
from .. import Usecase
|
||||
|
||||
|
||||
async def get_workspaces(self: 'Usecase', user_id: uuid.UUID) -> dto.GetWorkspacesOutput:
|
||||
async def get_workspaces(self: 'Usecase', user_id: uuid.UUID) -> list[dto.WorkspaceMembershipOutput]:
|
||||
memberships = await self.database.get_user_workspaces(user_id)
|
||||
|
||||
return dto.GetWorkspacesOutput(
|
||||
workspaces=[
|
||||
dto.WorkspaceMembershipOutput(
|
||||
id=membership.workspace_id,
|
||||
name=membership.workspace.name,
|
||||
)
|
||||
for membership in memberships
|
||||
]
|
||||
)
|
||||
return [
|
||||
dto.WorkspaceMembershipOutput(
|
||||
id=membership.workspace_id,
|
||||
name=membership.workspace.name,
|
||||
)
|
||||
for membership in memberships
|
||||
]
|
||||
|
||||
16
uv.lock
generated
16
uv.lock
generated
@@ -268,6 +268,20 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/dd/2c/42277afc1ba1a18f8358561eee40785d27becab8f80a1f945c0a3051c6eb/fastapi-0.121.0-py3-none-any.whl", hash = "sha256:8bdf1b15a55f4e4b0d6201033da9109ea15632cb76cf156e7b8b4019f2172106", size = 109183, upload-time = "2025-11-03T10:25:53.27Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastapi-pagination"
|
||||
version = "0.15.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "fastapi" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/72/be/e5edfb47d0253b5dc019ad0430cc26b34f9b29a21456abfb8ea0cff40782/fastapi_pagination-0.15.3.tar.gz", hash = "sha256:0667c3e31eb0c47f15e2d4d0a971490beed9b65a1079158b5ad0115488a370e2", size = 571922, upload-time = "2025-12-11T21:53:43.297Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/99/11a13d3b2ff6fb716fbe8a1da8f15da332472f13bea84377e1d070cf3a6c/fastapi_pagination-0.15.3-py3-none-any.whl", hash = "sha256:6c0e8b3265270bfa46a580f7a3a24559b0825917625b5c4ed3a1cd60a173552f", size = 56231, upload-time = "2025-12-11T21:53:44.257Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frozenlist"
|
||||
version = "1.8.0"
|
||||
@@ -1012,6 +1026,7 @@ dependencies = [
|
||||
{ name = "asyncpg" },
|
||||
{ name = "beautifulsoup4" },
|
||||
{ name = "fastapi" },
|
||||
{ name = "fastapi-pagination" },
|
||||
{ name = "httpx" },
|
||||
{ name = "lxml" },
|
||||
{ name = "pydantic-settings" },
|
||||
@@ -1042,6 +1057,7 @@ requires-dist = [
|
||||
{ name = "asyncpg", specifier = ">=0.30.0" },
|
||||
{ name = "beautifulsoup4", specifier = ">=4.14.2" },
|
||||
{ name = "fastapi", specifier = ">=0.121.0" },
|
||||
{ name = "fastapi-pagination", specifier = ">=0.15.3" },
|
||||
{ name = "httpx", specifier = ">=0.28.1" },
|
||||
{ name = "lxml", specifier = ">=6.0.2" },
|
||||
{ name = "pydantic-settings", specifier = ">=2.11.0" },
|
||||
|
||||
Reference in New Issue
Block a user