refactor
This commit is contained in:
@@ -311,105 +311,6 @@ class Postgres(DatabaseBase, Database):
|
||||
project = await domain.Project.get_or_none(channel_id=channel_id, workspace_id=workspace_id)
|
||||
return project is not None
|
||||
|
||||
async def get_active_purchase(self, project_id: uuid.UUID, creative_id: uuid.UUID) -> domain.Purchase | None:
|
||||
return await domain.Purchase.get_or_none(
|
||||
project_id=project_id, creative_id=creative_id, status=domain.PurchaseStatus.ACTIVE
|
||||
)
|
||||
|
||||
async def get_purchase(self, workspace_id: uuid.UUID, purchase_id: uuid.UUID) -> domain.Purchase | None:
|
||||
return await domain.Purchase.get_or_none(id=purchase_id, project__workspace_id=workspace_id)
|
||||
|
||||
async def get_project_purchases(self, workspace_id: uuid.UUID, project_id: uuid.UUID) -> list[domain.Purchase]:
|
||||
return (
|
||||
await domain.Purchase.filter(project__workspace_id=workspace_id, project_id=project_id)
|
||||
.prefetch_related('channels', 'channels__channel')
|
||||
.all()
|
||||
)
|
||||
|
||||
async def create_purchase(self, purchase: domain.Purchase) -> None:
|
||||
await purchase.save()
|
||||
|
||||
async def get_purchase_channels(self, purchase_id: uuid.UUID) -> list[domain.PurchaseChannel]:
|
||||
return await domain.PurchaseChannel.filter(purchase_id=purchase_id).prefetch_related('channel').all()
|
||||
|
||||
async def get_purchase_channel(
|
||||
self, purchase_channel_id: uuid.UUID, purchase_id: uuid.UUID
|
||||
) -> domain.PurchaseChannel | None:
|
||||
return (
|
||||
await domain.PurchaseChannel.filter(id=purchase_channel_id, purchase_id=purchase_id)
|
||||
.prefetch_related('channel')
|
||||
.first()
|
||||
)
|
||||
|
||||
async def get_purchase_channel_by_id(self, purchase_channel_id: uuid.UUID) -> domain.PurchaseChannel | None:
|
||||
return (
|
||||
await domain.PurchaseChannel.filter(id=purchase_channel_id)
|
||||
.prefetch_related('channel', 'purchase', 'purchase__project')
|
||||
.first()
|
||||
)
|
||||
|
||||
async def get_purchase_channels_by_project(self, project_id: uuid.UUID) -> list[domain.PurchaseChannel]:
|
||||
return (
|
||||
await domain.PurchaseChannel.filter(
|
||||
purchase__project_id=project_id, purchase__status=domain.PurchaseStatus.ACTIVE
|
||||
)
|
||||
.prefetch_related('channel')
|
||||
.all()
|
||||
)
|
||||
|
||||
async def add_channel_to_purchase(
|
||||
self,
|
||||
purchase_id: uuid.UUID,
|
||||
channel_id: uuid.UUID,
|
||||
invite_link: str,
|
||||
invite_link_type: domain.purchase.InviteLinkType,
|
||||
*,
|
||||
status: domain.PurchaseChannelStatus | None = None,
|
||||
comment: str | None = None,
|
||||
placement_at: datetime.datetime | None = None,
|
||||
cost_type: domain.purchase.CostType | None = None,
|
||||
cost_value: float | None = None,
|
||||
cost_before_bargain: float | None = None,
|
||||
format: str | None = None,
|
||||
) -> domain.PurchaseChannel:
|
||||
defaults: dict[str, object | None] = {
|
||||
'invite_link': invite_link,
|
||||
'invite_link_type': invite_link_type,
|
||||
}
|
||||
if status is not None:
|
||||
defaults['status'] = status
|
||||
if comment is not None:
|
||||
defaults['comment'] = comment
|
||||
if placement_at is not None:
|
||||
defaults['placement_at'] = placement_at
|
||||
if cost_type is not None:
|
||||
defaults['cost_type'] = cost_type
|
||||
if cost_value is not None:
|
||||
defaults['cost_value'] = cost_value
|
||||
if cost_before_bargain is not None:
|
||||
defaults['cost_before_bargain'] = cost_before_bargain
|
||||
if format is not None:
|
||||
defaults['format'] = format
|
||||
|
||||
purchase_channel, created = await domain.PurchaseChannel.get_or_create(
|
||||
purchase_id=purchase_id,
|
||||
channel_id=channel_id,
|
||||
defaults=defaults,
|
||||
)
|
||||
|
||||
if not created and defaults:
|
||||
for field, value in defaults.items():
|
||||
setattr(purchase_channel, field, value)
|
||||
await purchase_channel.save()
|
||||
|
||||
return purchase_channel
|
||||
|
||||
async def update_purchase_channel(self, purchase_channel: domain.PurchaseChannel) -> None:
|
||||
await purchase_channel.save()
|
||||
|
||||
async def remove_channel_from_purchase(self, purchase_id: uuid.UUID, channel_id: uuid.UUID) -> None:
|
||||
await domain.PurchaseChannel.filter(purchase_id=purchase_id, channel_id=channel_id).delete()
|
||||
|
||||
async def get_creative(self, workspace_id: uuid.UUID, creative_id: uuid.UUID) -> domain.Creative | None:
|
||||
return await domain.Creative.get_or_none(
|
||||
id=creative_id, project__workspace_id=workspace_id
|
||||
@@ -442,26 +343,51 @@ class Postgres(DatabaseBase, Database):
|
||||
async def delete_creative(self, creative_id: uuid.UUID) -> None:
|
||||
await domain.Creative.filter(id=creative_id).delete()
|
||||
|
||||
# Placement methods (user-managed, запланированные размещения)
|
||||
async def create_placement(self, placement: domain.Placement) -> None:
|
||||
await placement.save()
|
||||
|
||||
async def create_placements(self, placements: list[domain.Placement]) -> None:
|
||||
if placements:
|
||||
await domain.Placement.bulk_create(placements)
|
||||
|
||||
async def get_placement(self, workspace_id: uuid.UUID, placement_id: uuid.UUID) -> domain.Placement | None:
|
||||
return await domain.Placement.get_or_none(
|
||||
id=placement_id, project__workspace_id=workspace_id
|
||||
).prefetch_related('project', 'project__channel', 'channel', 'creative')
|
||||
|
||||
async def get_project_placements(
|
||||
self, workspace_id: uuid.UUID, project_id: uuid.UUID, include_archived: bool = False
|
||||
) -> list[domain.Placement]:
|
||||
query = domain.Placement.filter(project__workspace_id=workspace_id, project_id=project_id)
|
||||
|
||||
if not include_archived:
|
||||
query = query.filter(status__in=[domain.PlacementStatus.APPROVED, domain.PlacementStatus.IN_PROGRESS])
|
||||
|
||||
return (
|
||||
await query.prefetch_related('project', 'project__channel', 'channel', 'creative')
|
||||
.order_by('-created_at')
|
||||
.all()
|
||||
)
|
||||
|
||||
async def update_placement_user(self, placement: domain.Placement) -> None:
|
||||
await placement.save()
|
||||
|
||||
# Publication methods (system-managed, фактически размещенные посты)
|
||||
async def get_publication(self, workspace_id: uuid.UUID, publication_id: uuid.UUID) -> domain.Publication | None:
|
||||
return await domain.Publication.get_or_none(
|
||||
id=publication_id, placement__project__workspace_id=workspace_id
|
||||
).prefetch_related(
|
||||
'project',
|
||||
'project__channel',
|
||||
'purchase_channel',
|
||||
'purchase_channel__channel',
|
||||
'creative',
|
||||
'placement',
|
||||
'placement__project',
|
||||
'placement__project__channel',
|
||||
'placement__channel',
|
||||
'placement__creative',
|
||||
'post',
|
||||
'post__channel',
|
||||
)
|
||||
|
||||
async def create_placement(self, placement: domain.Placement) -> None:
|
||||
await placement.save()
|
||||
|
||||
async def update_placement(self, placement: domain.Placement) -> None:
|
||||
await placement.save()
|
||||
|
||||
async def get_workspace_placements(
|
||||
async def get_workspace_publications(
|
||||
self,
|
||||
workspace_id: uuid.UUID,
|
||||
project_id: uuid.UUID | None = None,
|
||||
@@ -471,75 +397,85 @@ class Postgres(DatabaseBase, Database):
|
||||
allowed_project_ids: set[uuid.UUID] | None = None,
|
||||
date_from: datetime.datetime | None = None,
|
||||
date_to: datetime.datetime | None = None,
|
||||
) -> list[domain.Placement]:
|
||||
query = domain.Placement.filter(project__workspace_id=workspace_id)
|
||||
) -> list[domain.Publication]:
|
||||
query = domain.Publication.filter(placement__project__workspace_id=workspace_id)
|
||||
|
||||
if project_id:
|
||||
query = query.filter(project_id=project_id)
|
||||
query = query.filter(placement__project_id=project_id)
|
||||
elif allowed_project_ids is not None:
|
||||
if not allowed_project_ids:
|
||||
return []
|
||||
query = query.filter(project_id__in=list(allowed_project_ids))
|
||||
query = query.filter(placement__project_id__in=list(allowed_project_ids))
|
||||
if placement_channel_id:
|
||||
query = query.filter(purchase_channel__channel_id=placement_channel_id)
|
||||
query = query.filter(placement__channel_id=placement_channel_id)
|
||||
if creative_id:
|
||||
query = query.filter(creative_id=creative_id)
|
||||
query = query.filter(placement__creative_id=creative_id)
|
||||
|
||||
if date_from:
|
||||
query = query.filter(wanted_placement_date__gte=date_from)
|
||||
query = query.filter(created_at__gte=date_from)
|
||||
if date_to:
|
||||
query = query.filter(wanted_placement_date__lte=date_to)
|
||||
query = query.filter(created_at__lte=date_to)
|
||||
|
||||
if not include_archived:
|
||||
query = query.filter(status=domain.PlacementStatus.ACTIVE)
|
||||
query = query.filter(status=domain.PublicationStatus.ACTIVE)
|
||||
|
||||
return (
|
||||
await query.prefetch_related(
|
||||
'project',
|
||||
'project__channel',
|
||||
'purchase_channel',
|
||||
'purchase_channel__channel',
|
||||
'purchase_channel__purchase',
|
||||
'creative',
|
||||
'placement',
|
||||
'placement__project',
|
||||
'placement__project__channel',
|
||||
'placement__channel',
|
||||
'placement__creative',
|
||||
'post',
|
||||
'post__channel',
|
||||
)
|
||||
.order_by('-wanted_placement_date')
|
||||
.order_by('-created_at')
|
||||
.all()
|
||||
)
|
||||
|
||||
async def delete_placement(self, placement_id: uuid.UUID) -> None:
|
||||
await domain.Placement.filter(id=placement_id).delete()
|
||||
async def create_publication(self, publication: domain.Publication) -> None:
|
||||
await publication.save()
|
||||
|
||||
async def get_placement_by_invite_link(self, invite_link: str) -> domain.Placement | None:
|
||||
return await domain.Placement.get_or_none(
|
||||
purchase_channel__invite_link=invite_link
|
||||
).prefetch_related('project', 'project__channel', 'purchase_channel', 'purchase_channel__channel', 'creative')
|
||||
async def update_publication(self, publication: domain.Publication) -> None:
|
||||
await publication.save()
|
||||
|
||||
async def delete_publication(self, publication_id: uuid.UUID) -> None:
|
||||
await domain.Publication.filter(id=publication_id).delete()
|
||||
|
||||
async def get_publication_by_invite_link(self, invite_link: str) -> domain.Publication | None:
|
||||
return await domain.Publication.get_or_none(placement__invite_link=invite_link).prefetch_related(
|
||||
'placement',
|
||||
'placement__project',
|
||||
'placement__project__channel',
|
||||
'placement__channel',
|
||||
'placement__creative',
|
||||
)
|
||||
|
||||
# Subscription methods
|
||||
async def create_subscription(self, subscription: domain.Subscription) -> None:
|
||||
await subscription.save()
|
||||
|
||||
async def get_subscription_by_subscriber_and_placement(
|
||||
self, telegram_user_id: uuid.UUID, placement_id: uuid.UUID
|
||||
async def get_subscription_by_subscriber_and_publication(
|
||||
self, telegram_user_id: uuid.UUID, publication_id: uuid.UUID
|
||||
) -> domain.Subscription | None:
|
||||
return await domain.Subscription.get_or_none(
|
||||
telegram_user_id=telegram_user_id, placement_id=placement_id
|
||||
telegram_user_id=telegram_user_id, publication_id=publication_id
|
||||
)
|
||||
|
||||
async def update_subscription(self, subscription: domain.Subscription) -> None:
|
||||
await subscription.save()
|
||||
|
||||
async def get_subscriptions_for_placements(
|
||||
async def get_subscriptions_for_publications(
|
||||
self,
|
||||
placement_ids: list[uuid.UUID],
|
||||
publication_ids: list[uuid.UUID],
|
||||
*,
|
||||
date_from: datetime.datetime | None = None,
|
||||
date_to: datetime.datetime | None = None,
|
||||
) -> list[domain.Subscription]:
|
||||
if not placement_ids:
|
||||
if not publication_ids:
|
||||
return []
|
||||
|
||||
query = domain.Subscription.filter(placement_id__in=placement_ids)
|
||||
query = domain.Subscription.filter(publication_id__in=publication_ids)
|
||||
|
||||
if date_from:
|
||||
query = query.filter(created_at__gte=date_from)
|
||||
@@ -554,10 +490,10 @@ class Postgres(DatabaseBase, Database):
|
||||
return (
|
||||
await domain.Subscription.filter(
|
||||
telegram_user_id=telegram_user_id,
|
||||
placement__project_id=project_id,
|
||||
publication__placement__project_id=project_id,
|
||||
status=domain.SubscriptionStatus.ACTIVE,
|
||||
)
|
||||
.prefetch_related('placement', 'telegram_user')
|
||||
.prefetch_related('publication', 'telegram_user')
|
||||
.all()
|
||||
)
|
||||
|
||||
@@ -567,10 +503,10 @@ class Postgres(DatabaseBase, Database):
|
||||
return (
|
||||
await domain.Subscription.filter(
|
||||
telegram_user_id=telegram_user_id,
|
||||
placement__project_id=project_id,
|
||||
publication__placement__project_id=project_id,
|
||||
status=domain.SubscriptionStatus.ACTIVE,
|
||||
)
|
||||
.prefetch_related('placement', 'telegram_user')
|
||||
.prefetch_related('publication', 'telegram_user')
|
||||
.first()
|
||||
)
|
||||
|
||||
@@ -625,43 +561,43 @@ class Postgres(DatabaseBase, Database):
|
||||
return results
|
||||
|
||||
# Count methods
|
||||
async def count_placements_by_creative(self, creative_id: uuid.UUID) -> int:
|
||||
return await domain.Placement.filter(creative_id=creative_id).count()
|
||||
async def count_publications_by_creative(self, creative_id: uuid.UUID) -> int:
|
||||
return await domain.Publication.filter(placement__creative_id=creative_id).count()
|
||||
|
||||
async def count_subscriptions_by_placement(self, placement_id: uuid.UUID) -> int:
|
||||
return await domain.Subscription.filter(placement_id=placement_id).count()
|
||||
async def count_subscriptions_by_publication(self, publication_id: uuid.UUID) -> int:
|
||||
return await domain.Subscription.filter(publication_id=publication_id).count()
|
||||
|
||||
async def count_placements_by_creative_batch(self, creative_ids: list[uuid.UUID]) -> dict[uuid.UUID, int]:
|
||||
async def count_publications_by_creative_batch(self, creative_ids: list[uuid.UUID]) -> dict[uuid.UUID, int]:
|
||||
if not creative_ids:
|
||||
return {}
|
||||
|
||||
from tortoise.functions import Count
|
||||
|
||||
results = (
|
||||
await domain.Placement.filter(creative_id__in=creative_ids)
|
||||
.group_by('creative_id')
|
||||
await domain.Publication.filter(placement__creative_id__in=creative_ids)
|
||||
.group_by('placement__creative_id')
|
||||
.annotate(count=Count('id'))
|
||||
.values('creative_id', 'count')
|
||||
.values('placement__creative_id', 'count')
|
||||
)
|
||||
|
||||
counts = {row['creative_id']: row['count'] for row in results}
|
||||
counts = {row['placement__creative_id']: row['count'] for row in results}
|
||||
return {cid: counts.get(cid, 0) for cid in creative_ids}
|
||||
|
||||
async def count_subscriptions_by_placement_batch(self, placement_ids: list[uuid.UUID]) -> dict[uuid.UUID, int]:
|
||||
if not placement_ids:
|
||||
async def count_subscriptions_by_publication_batch(self, publication_ids: list[uuid.UUID]) -> dict[uuid.UUID, int]:
|
||||
if not publication_ids:
|
||||
return {}
|
||||
|
||||
from tortoise.functions import Count
|
||||
|
||||
results = (
|
||||
await domain.Subscription.filter(placement_id__in=placement_ids)
|
||||
.group_by('placement_id')
|
||||
await domain.Subscription.filter(publication_id__in=publication_ids)
|
||||
.group_by('publication_id')
|
||||
.annotate(count=Count('id'))
|
||||
.values('placement_id', 'count')
|
||||
.values('publication_id', 'count')
|
||||
)
|
||||
|
||||
counts = {row['placement_id']: row['count'] for row in results}
|
||||
return {pid: counts.get(pid, 0) for pid in placement_ids}
|
||||
counts = {row['publication_id']: row['count'] for row in results}
|
||||
return {pid: counts.get(pid, 0) for pid in publication_ids}
|
||||
|
||||
async def has_placements_for_creative(self, creative_id: uuid.UUID) -> bool:
|
||||
return await domain.Placement.filter(creative_id=creative_id).exists()
|
||||
async def has_publications_for_creative(self, creative_id: uuid.UUID) -> bool:
|
||||
return await domain.Publication.filter(placement__creative_id=creative_id).exists()
|
||||
|
||||
Reference in New Issue
Block a user