feat: убираем денормализацию (антипреждевременная оптимизация)
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
# ruff: noqa
|
||||
# mypy: ignore-errors
|
||||
from tortoise import BaseDBAsyncClient
|
||||
|
||||
RUN_IN_TRANSACTION = True
|
||||
@@ -244,74 +246,74 @@ async def downgrade(db: BaseDBAsyncClient) -> str:
|
||||
|
||||
|
||||
MODELS_STATE = (
|
||||
"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"
|
||||
"37jnCt9x2t7SO8Gzcb/f4/gXYk2A=="
|
||||
'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'
|
||||
'37jnCt9x2t7SO8Gzcb/f4/gXYk2A=='
|
||||
)
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
from tortoise import BaseDBAsyncClient
|
||||
|
||||
RUN_IN_TRANSACTION = True
|
||||
|
||||
|
||||
async def upgrade(db: BaseDBAsyncClient) -> str:
|
||||
return """
|
||||
COMMENT ON COLUMN "placement"."status" IS 'ACTIVE: active
|
||||
ARCHIVED: archived';"""
|
||||
|
||||
|
||||
async def downgrade(db: BaseDBAsyncClient) -> str:
|
||||
return """
|
||||
COMMENT ON COLUMN "placement"."status" IS 'PENDING: pending
|
||||
ACTIVE: active
|
||||
DELETED: deleted
|
||||
ARCHIVED: archived';"""
|
||||
|
||||
|
||||
MODELS_STATE = (
|
||||
"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"
|
||||
)
|
||||
98
migrations/models/1_20251216100730_update.py
Normal file
98
migrations/models/1_20251216100730_update.py
Normal file
@@ -0,0 +1,98 @@
|
||||
# ruff: noqa
|
||||
# mypy: ignore-errors
|
||||
from tortoise import BaseDBAsyncClient
|
||||
|
||||
RUN_IN_TRANSACTION = True
|
||||
|
||||
|
||||
async def upgrade(db: BaseDBAsyncClient) -> str:
|
||||
return """
|
||||
ALTER TABLE "creative" DROP COLUMN "placements_count";
|
||||
ALTER TABLE "placement" DROP COLUMN "subscriptions_count";
|
||||
COMMENT ON COLUMN "placement"."status" IS 'ACTIVE: active
|
||||
ARCHIVED: archived';
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "uid_subscriptio_placeme_8fcb8f" ON "subscription" ("placement_id", "subscriber_id");"""
|
||||
|
||||
|
||||
async def downgrade(db: BaseDBAsyncClient) -> str:
|
||||
return """
|
||||
DROP INDEX IF EXISTS "uid_subscriptio_placeme_8fcb8f";
|
||||
ALTER TABLE "creative" ADD "placements_count" INT NOT NULL DEFAULT 0;
|
||||
ALTER TABLE "placement" ADD "subscriptions_count" INT NOT NULL DEFAULT 0;
|
||||
COMMENT ON COLUMN "placement"."status" IS 'PENDING: pending
|
||||
ACTIVE: active
|
||||
DELETED: deleted
|
||||
ARCHIVED: archived';"""
|
||||
|
||||
|
||||
MODELS_STATE = (
|
||||
'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'
|
||||
'aq33Ha3tI7wbNxv9/j9vKFN0'
|
||||
)
|
||||
@@ -497,3 +497,45 @@ class Postgres(DatabaseBase):
|
||||
|
||||
async def clear_telegram_state(self, telegram_id: int) -> None:
|
||||
await domain.TelegramState.filter(telegram_id=telegram_id).delete()
|
||||
|
||||
# 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_subscriptions_by_placement(self, placement_id: uuid.UUID) -> int:
|
||||
return await domain.Subscription.filter(placement_id=placement_id).count()
|
||||
|
||||
async def count_placements_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')
|
||||
.annotate(count=Count('id'))
|
||||
.values('creative_id', 'count')
|
||||
)
|
||||
|
||||
counts = {row['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:
|
||||
return {}
|
||||
|
||||
from tortoise.functions import Count
|
||||
|
||||
results = (
|
||||
await domain.Subscription.filter(placement_id__in=placement_ids)
|
||||
.group_by('placement_id')
|
||||
.annotate(count=Count('id'))
|
||||
.values('placement_id', 'count')
|
||||
)
|
||||
|
||||
counts = {row['placement_id']: row['count'] for row in results}
|
||||
return {pid: counts.get(pid, 0) for pid in placement_ids}
|
||||
|
||||
async def has_placements_for_creative(self, creative_id: uuid.UUID) -> bool:
|
||||
return await domain.Placement.filter(creative_id=creative_id).exists()
|
||||
|
||||
@@ -20,7 +20,6 @@ class Creative(TimestampedModel):
|
||||
name = fields.CharField(max_length=255)
|
||||
text = fields.TextField()
|
||||
status = fields.CharEnumField(CreativeStatus, default=CreativeStatus.ACTIVE)
|
||||
placements_count = fields.IntField(default=0)
|
||||
|
||||
workspace: fields.ForeignKeyRelation['Workspace'] = fields.ForeignKeyField(
|
||||
'models.Workspace', related_name='creatives', on_delete=fields.CASCADE, index=True
|
||||
|
||||
@@ -39,7 +39,6 @@ class Placement(TimestampedModel):
|
||||
invite_link = fields.CharField(max_length=512)
|
||||
|
||||
status = fields.CharEnumField(PlacementStatus, default=PlacementStatus.ACTIVE)
|
||||
subscriptions_count = fields.IntField(default=0)
|
||||
|
||||
project: fields.ForeignKeyRelation['Project'] = fields.ForeignKeyField(
|
||||
'models.Project', related_name='placements', on_delete=fields.CASCADE, index=True
|
||||
|
||||
@@ -34,3 +34,4 @@ class Subscription(TimestampedModel):
|
||||
|
||||
class Meta:
|
||||
table = 'subscription'
|
||||
unique_together = (('placement_id', 'subscriber_id'),)
|
||||
|
||||
@@ -230,6 +230,17 @@ class Database(typing.Protocol):
|
||||
self, subscriber_id: UUID, project_id: UUID
|
||||
) -> domain.Subscription | None: ...
|
||||
|
||||
# Count methods
|
||||
async def count_placements_by_creative(self, creative_id: UUID) -> int: ...
|
||||
|
||||
async def count_subscriptions_by_placement(self, placement_id: UUID) -> int: ...
|
||||
|
||||
async def count_placements_by_creative_batch(self, creative_ids: list[UUID]) -> dict[UUID, int]: ...
|
||||
|
||||
async def count_subscriptions_by_placement_batch(self, placement_ids: list[UUID]) -> dict[UUID, int]: ...
|
||||
|
||||
async def has_placements_for_creative(self, creative_id: UUID) -> bool: ...
|
||||
|
||||
# Post methods
|
||||
async def create_post(self, post: domain.Post) -> None: ...
|
||||
|
||||
|
||||
@@ -65,13 +65,17 @@ async def get_channel_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 {}
|
||||
|
||||
# Batch fetch subscriptions counts
|
||||
placement_ids = [p.id for p in placements]
|
||||
subscriptions_counts = await self.database.count_subscriptions_by_placement_batch(placement_ids)
|
||||
|
||||
for p in placements:
|
||||
stats = channel_stats.get(p.placement_channel_id)
|
||||
if stats is None:
|
||||
continue
|
||||
|
||||
stats.total_cost += p.cost if p.cost is not None else 0
|
||||
stats.total_subscriptions += p.subscriptions_count
|
||||
stats.total_subscriptions += subscriptions_counts.get(p.id, 0)
|
||||
|
||||
# Get views from batch data
|
||||
if p.post and p.post.id in views_map:
|
||||
|
||||
@@ -53,13 +53,17 @@ async def get_creatives_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 {}
|
||||
|
||||
# Batch fetch subscriptions counts
|
||||
placement_ids = [p.id for p in placements]
|
||||
subscriptions_counts = await self.database.count_subscriptions_by_placement_batch(placement_ids)
|
||||
|
||||
for p in placements:
|
||||
stats = creative_stats.get(p.creative_id)
|
||||
if stats is None:
|
||||
continue
|
||||
|
||||
stats.total_cost += p.cost if p.cost is not None else 0
|
||||
stats.total_subscriptions += p.subscriptions_count
|
||||
stats.total_subscriptions += subscriptions_counts.get(p.id, 0)
|
||||
|
||||
# Get views from batch data
|
||||
if p.post and p.post.id in views_map:
|
||||
|
||||
@@ -48,6 +48,10 @@ 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 {}
|
||||
|
||||
# Batch fetch subscriptions counts
|
||||
placement_ids = [p.id for p in placements]
|
||||
subscriptions_counts = await self.database.count_subscriptions_by_placement_batch(placement_ids)
|
||||
|
||||
return [
|
||||
dto.PlacementAnalyticsOutput(
|
||||
id=p.id,
|
||||
@@ -59,9 +63,9 @@ async def get_placements_analytics(
|
||||
creative_name=p.creative.name,
|
||||
placement_date=p.placement_date,
|
||||
cost=p.cost,
|
||||
subscriptions_count=p.subscriptions_count,
|
||||
subscriptions_count=subscriptions_counts.get(p.id, 0),
|
||||
views_count=views_map[p.post.id][0] if p.post and p.post.id in views_map else None,
|
||||
cpf=_calculate_cpf(p.cost, p.subscriptions_count),
|
||||
cpf=_calculate_cpf(p.cost, subscriptions_counts.get(p.id, 0)),
|
||||
cpm=_calculate_cpm(p.cost, views_map[p.post.id][0] if p.post and p.post.id in views_map else None),
|
||||
)
|
||||
for p in placements
|
||||
|
||||
@@ -74,6 +74,10 @@ async def get_spending_analytics(
|
||||
post_ids = [p.post.id for p in filtered if p.post]
|
||||
views_map = await self.database.get_latest_views_data_batch(post_ids) if post_ids else {}
|
||||
|
||||
# Batch fetch subscriptions counts
|
||||
placement_ids = [p.id for p in filtered]
|
||||
subscriptions_counts = await self.database.count_subscriptions_by_placement_batch(placement_ids)
|
||||
|
||||
# Группировка по периодам
|
||||
period_data: dict[str, PeriodData] = defaultdict(PeriodData)
|
||||
|
||||
@@ -85,8 +89,9 @@ async def get_spending_analytics(
|
||||
total_cost += p.cost
|
||||
pd.cost += p.cost
|
||||
|
||||
total_subs += p.subscriptions_count
|
||||
pd.subscriptions += p.subscriptions_count
|
||||
subs_count = subscriptions_counts.get(p.id, 0)
|
||||
total_subs += subs_count
|
||||
pd.subscriptions += subs_count
|
||||
|
||||
# Get views from batch data
|
||||
if p.post and p.post.id in views_map:
|
||||
|
||||
@@ -42,5 +42,5 @@ async def create_creative(
|
||||
project_channel_title=project.channel.title,
|
||||
created_at=creative.created_at,
|
||||
status=creative.status,
|
||||
placements_count=creative.placements_count,
|
||||
placements_count=0,
|
||||
)
|
||||
|
||||
@@ -21,7 +21,8 @@ async def delete_creative(self: 'Usecase', input: dto.DeleteCreativeInput) -> No
|
||||
|
||||
context.ensure_project_permission(domain.PermissionKey.PROJECTS_WRITE, creative.project_id)
|
||||
|
||||
if creative.placements_count > 0:
|
||||
has_placements = await self.database.has_placements_for_creative(creative.id)
|
||||
if has_placements:
|
||||
log.warning('Creative %s is used in placements and cannot be deleted', input.creative_id)
|
||||
raise domain.CreativeInUse(input.creative_id)
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ async def get_creative(self: 'Usecase', input: dto.GetCreativeInput) -> dto.Crea
|
||||
|
||||
context.ensure_project_permission(domain.PermissionKey.PROJECTS_READ, creative.project_id)
|
||||
|
||||
placements_count = await self.database.count_placements_by_creative(creative.id)
|
||||
|
||||
return dto.CreativeOutput(
|
||||
id=creative.id,
|
||||
name=creative.name,
|
||||
@@ -28,5 +30,5 @@ async def get_creative(self: 'Usecase', input: dto.GetCreativeInput) -> dto.Crea
|
||||
project_channel_title=creative.project.channel.title,
|
||||
created_at=creative.created_at,
|
||||
status=creative.status,
|
||||
placements_count=creative.placements_count,
|
||||
placements_count=placements_count,
|
||||
)
|
||||
|
||||
@@ -24,6 +24,9 @@ async def get_creatives(self: 'Usecase', input: dto.GetCreativesInput) -> list[d
|
||||
allowed_project_ids=allowed_project_ids,
|
||||
)
|
||||
|
||||
creative_ids = [c.id for c in creatives]
|
||||
placements_counts = await self.database.count_placements_by_creative_batch(creative_ids)
|
||||
|
||||
return [
|
||||
dto.CreativeOutput(
|
||||
id=creative.id,
|
||||
@@ -33,7 +36,7 @@ async def get_creatives(self: 'Usecase', input: dto.GetCreativesInput) -> list[d
|
||||
project_channel_title=creative.project.channel.title,
|
||||
created_at=creative.created_at,
|
||||
status=creative.status,
|
||||
placements_count=creative.placements_count,
|
||||
placements_count=placements_counts.get(creative.id, 0),
|
||||
)
|
||||
for creative in creatives
|
||||
]
|
||||
|
||||
@@ -35,6 +35,8 @@ async def update_creative(
|
||||
|
||||
await self.database.update_creative(creative)
|
||||
|
||||
placements_count = await self.database.count_placements_by_creative(creative.id)
|
||||
|
||||
return dto.CreativeOutput(
|
||||
id=creative.id,
|
||||
name=creative.name,
|
||||
@@ -43,5 +45,5 @@ async def update_creative(
|
||||
project_channel_title=creative.project.channel.title,
|
||||
created_at=creative.created_at,
|
||||
status=creative.status,
|
||||
placements_count=creative.placements_count,
|
||||
placements_count=placements_count,
|
||||
)
|
||||
|
||||
@@ -39,8 +39,6 @@ async def create_placement(
|
||||
if project.channel.telegram_id is None:
|
||||
raise domain.ChannelNotFound(project.channel.id)
|
||||
|
||||
print(f'Телега id: {project.channel.telegram_id}')
|
||||
|
||||
invite_link = await self.telegram_bot.create_chat_invite_link(project.channel.telegram_id, requires_approval)
|
||||
|
||||
placement = domain.Placement(
|
||||
@@ -56,12 +54,8 @@ async def create_placement(
|
||||
status=domain.PlacementStatus.ACTIVE,
|
||||
)
|
||||
|
||||
async with self.database.transaction():
|
||||
await self.database.create_placement(placement)
|
||||
|
||||
creative.placements_count += 1
|
||||
await self.database.update_creative(creative)
|
||||
|
||||
return dto.PlacementOutput(
|
||||
id=placement.id,
|
||||
project_id=placement.project_id,
|
||||
@@ -77,6 +71,6 @@ async def create_placement(
|
||||
invite_link_type=placement.invite_link_type,
|
||||
invite_link=placement.invite_link,
|
||||
status=placement.status,
|
||||
subscriptions_count=placement.subscriptions_count,
|
||||
subscriptions_count=0,
|
||||
created_at=placement.created_at,
|
||||
)
|
||||
|
||||
@@ -21,14 +21,4 @@ async def delete_placement(self: 'Usecase', input: dto.DeletePlacementInput) ->
|
||||
|
||||
context.ensure_project_permission(domain.PermissionKey.PLACEMENTS_WRITE, placement.project_id)
|
||||
|
||||
if placement.status != domain.PlacementStatus.ACTIVE:
|
||||
await self.database.delete_placement(input.placement_id)
|
||||
return
|
||||
|
||||
async with self.database.transaction():
|
||||
creative = await self.database.get_creative(input.workspace_id, placement.creative_id)
|
||||
if creative and creative.placements_count > 0:
|
||||
creative.placements_count -= 1
|
||||
await self.database.update_creative(creative)
|
||||
|
||||
await self.database.delete_placement(input.placement_id)
|
||||
|
||||
@@ -12,7 +12,6 @@ log = logging.getLogger(__name__)
|
||||
async def fetch_placement_post_cycle(self: 'Usecase', interval_seconds: int) -> None:
|
||||
log.debug('Starting fetch_placement_post_cycle')
|
||||
|
||||
# Получаем все placement без привязанного поста
|
||||
placements_without_post = (
|
||||
await domain.Placement.filter(
|
||||
post_id__isnull=True,
|
||||
|
||||
@@ -25,18 +25,11 @@ async def get_placement(self: 'Usecase', input: dto.GetPlacementInput) -> dto.Pl
|
||||
|
||||
# Compute post-related fields
|
||||
ad_post_url = None
|
||||
views_count = None
|
||||
views_availability = domain.PostViewsAvailability.UNKNOWN
|
||||
last_views_fetch_at = None
|
||||
|
||||
if placement.post:
|
||||
ad_post_url = generate_post_url(placement.post.channel.username, placement.post.message_id)
|
||||
|
||||
# Get latest views data
|
||||
views_data = await self.database.get_latest_views_data(placement.post.id)
|
||||
if views_data:
|
||||
views_count, last_views_fetch_at = views_data
|
||||
views_availability = domain.PostViewsAvailability.AVAILABLE
|
||||
subscriptions_count = await self.database.count_subscriptions_by_placement(placement.id)
|
||||
|
||||
return dto.PlacementOutput(
|
||||
id=placement.id,
|
||||
@@ -53,6 +46,6 @@ async def get_placement(self: 'Usecase', input: dto.GetPlacementInput) -> dto.Pl
|
||||
invite_link_type=placement.invite_link_type,
|
||||
invite_link=placement.invite_link,
|
||||
status=placement.status,
|
||||
subscriptions_count=placement.subscriptions_count,
|
||||
subscriptions_count=subscriptions_count,
|
||||
created_at=placement.created_at,
|
||||
)
|
||||
|
||||
@@ -28,6 +28,9 @@ async def get_placements(self: 'Usecase', input: dto.GetPlacementsInput) -> list
|
||||
allowed_project_ids=allowed_project_ids,
|
||||
)
|
||||
|
||||
placement_ids = [p.id for p in placements]
|
||||
subscriptions_counts = await self.database.count_subscriptions_by_placement_batch(placement_ids)
|
||||
|
||||
placement_outputs = []
|
||||
for placement in placements:
|
||||
ad_post_url = None
|
||||
@@ -51,7 +54,7 @@ async def get_placements(self: 'Usecase', input: dto.GetPlacementsInput) -> list
|
||||
invite_link_type=placement.invite_link_type,
|
||||
invite_link=placement.invite_link,
|
||||
status=placement.status,
|
||||
subscriptions_count=placement.subscriptions_count,
|
||||
subscriptions_count=subscriptions_counts.get(placement.id, 0),
|
||||
created_at=placement.created_at,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -44,6 +44,8 @@ async def update_placement(
|
||||
if placement.post:
|
||||
ad_post_url = generate_post_url(placement.post.channel.username, placement.post.message_id)
|
||||
|
||||
subscriptions_count = await self.database.count_subscriptions_by_placement(placement.id)
|
||||
|
||||
return dto.PlacementOutput(
|
||||
id=placement.id,
|
||||
project_id=placement.project_id,
|
||||
@@ -59,6 +61,6 @@ async def update_placement(
|
||||
invite_link_type=placement.invite_link_type,
|
||||
invite_link=placement.invite_link,
|
||||
status=placement.status,
|
||||
subscriptions_count=placement.subscriptions_count,
|
||||
subscriptions_count=subscriptions_count,
|
||||
created_at=placement.created_at,
|
||||
)
|
||||
|
||||
@@ -73,9 +73,18 @@ async def tg_add_project_1(self: 'Usecase', input: dto.ConnectProjectInput) -> d
|
||||
channel = await self.database.get_channel(telegram_id=input.telegram_id)
|
||||
if channel:
|
||||
channel.title = input.title
|
||||
if input.username is not None:
|
||||
channel.username = input.username
|
||||
await self.database.update_channel(channel)
|
||||
else:
|
||||
if input.username is None:
|
||||
log.warning('Cannot create channel %s without username', input.telegram_id)
|
||||
await self.telegram_bot.send_message(
|
||||
f'⚠️ Канал "{input.title}" не может быть подключен.\n\nУ канала отсутствует публичный username.',
|
||||
input.user_telegram_id,
|
||||
)
|
||||
return None
|
||||
|
||||
channel = domain.Channel(
|
||||
telegram_id=input.telegram_id,
|
||||
title=input.title,
|
||||
|
||||
@@ -60,14 +60,10 @@ async def handle_subscription(
|
||||
existing_sub = await self.database.get_subscription_by_subscriber_and_placement(subscriber.id, placement.id)
|
||||
|
||||
if existing_sub and existing_sub.status == domain.SubscriptionStatus.UNSUBSCRIBED:
|
||||
async with self.database.transaction():
|
||||
existing_sub.status = domain.SubscriptionStatus.ACTIVE
|
||||
existing_sub.unsubscribed_at = None
|
||||
await self.database.update_subscription(existing_sub)
|
||||
|
||||
placement.subscriptions_count += 1
|
||||
await self.database.update_placement(placement)
|
||||
|
||||
log.info(
|
||||
'Subscription reactivated: subscriber %s (telegram_id: %s) resubscribed via same placement %s',
|
||||
subscriber.id,
|
||||
@@ -81,12 +77,8 @@ async def handle_subscription(
|
||||
subscriber_id=subscriber.id,
|
||||
invite_link=invite_link,
|
||||
)
|
||||
async with self.database.transaction():
|
||||
await self.database.create_subscription(subscription)
|
||||
|
||||
placement.subscriptions_count += 1
|
||||
await self.database.update_placement(placement)
|
||||
|
||||
log.info(
|
||||
'Subscription created: subscriber %s (telegram_id: %s) subscribed via placement %s (invite_link: %s)',
|
||||
subscriber.id,
|
||||
|
||||
@@ -34,19 +34,13 @@ async def handle_unsubscription(self: 'Usecase', user_telegram_id: int, channel_
|
||||
return
|
||||
|
||||
for subscription in subscriptions:
|
||||
async with self.database.transaction():
|
||||
subscription.status = domain.SubscriptionStatus.UNSUBSCRIBED
|
||||
subscription.unsubscribed_at = timezone.now()
|
||||
await self.database.update_subscription(subscription)
|
||||
|
||||
placement = subscription.placement
|
||||
if placement.subscriptions_count > 0:
|
||||
placement.subscriptions_count -= 1
|
||||
await self.database.update_placement(placement)
|
||||
|
||||
log.info(
|
||||
'Subscription marked as unsubscribed: subscriber %s (telegram_id: %s) unsubscribed from placement %s',
|
||||
subscriber.id,
|
||||
user_telegram_id,
|
||||
placement.id,
|
||||
subscription.placement_id,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user