This commit is contained in:
Artem Tsyrulnikov
2026-01-15 22:02:16 +03:00
parent 6ec26c96e3
commit 049024ebe8
72 changed files with 2417 additions and 2054 deletions

View File

@@ -60,20 +60,20 @@ def _format_period_label(dt: datetime.datetime, grouping: dto.DateGrouping) -> s
raise ValueError('Invalid date grouping')
def _get_grouping_date(placement: domain.Placement, date_grouping: dto.DateGroupingType) -> datetime.datetime:
def _get_grouping_date(publication: domain.Publication, date_grouping: dto.DateGroupingType) -> datetime.datetime:
match date_grouping:
case dto.DateGroupingType.PLACEMENT_DATE:
return placement.wanted_placement_date
return publication.wanted_placement_date
case dto.DateGroupingType.PURCHASE_DATE:
if placement.purchase_channel and placement.purchase_channel.purchase:
return placement.purchase_channel.purchase.created_at
return placement.wanted_placement_date # Fallback
if publication.placement:
return publication.placement.created_at
return publication.wanted_placement_date # Fallback
case dto.DateGroupingType.LINK_DATE:
if placement.purchase_channel:
return placement.purchase_channel.created_at
return placement.wanted_placement_date # Fallback
if publication.placement:
return publication.placement.created_at
return publication.wanted_placement_date # Fallback
case _:
return placement.wanted_placement_date
return publication.wanted_placement_date
@dataclass
@@ -96,7 +96,7 @@ def _calculate_metrics(
all_metrics = requested_metrics is None or len(requested_metrics) == 0
def should_include(metric: dto.ProjectMetrics) -> bool:
return all_metrics or metric in requested_metrics
return all_metrics or (requested_metrics is not None and metric in requested_metrics)
metrics = dto.ProjectMetricsData()
@@ -183,7 +183,7 @@ async def get_projects_analytics(
else:
allowed_project_ids = set(input.project_ids)
placements = await self.database.get_workspace_placements(
placements = await self.database.get_workspace_publications(
input.workspace_id,
project_id=None,
include_archived=False,
@@ -194,28 +194,28 @@ async def get_projects_analytics(
# Фильтрация по датам на основе date_grouping
filtered_placements = []
for placement in placements:
grouping_date = _get_grouping_date(placement, input.date_grouping)
for publication in placements:
grouping_date = _get_grouping_date(publication, input.date_grouping)
if input.date_from and grouping_date < input.date_from:
continue
if input.date_to and grouping_date > input.date_to:
continue
filtered_placements.append(placement)
filtered_placements.append(publication)
# Batch fetch views data
post_ids = [p.post.id for p in filtered_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 filtered_placements]
subscriptions_counts = await self.database.count_subscriptions_by_placement_batch(placement_ids)
publication_ids = [p.id for p in filtered_placements]
subscriptions_counts = await self.database.count_subscriptions_by_publication_batch(publication_ids)
# Группировка по периодам
period_data: dict[str, PeriodMetrics] = defaultdict(PeriodMetrics)
total_metrics = PeriodMetrics()
for placement in filtered_placements:
grouping_date = _get_grouping_date(placement, input.date_grouping)
for publication in filtered_placements:
grouping_date = _get_grouping_date(publication, input.date_grouping)
period = _format_period(grouping_date, input.grouping)
pd = period_data[period]
@@ -223,29 +223,29 @@ async def get_projects_analytics(
pd.purchases_count += 1
total_metrics.purchases_count += 1
cost = placement.cost or 0.0
cost = publication.cost or 0.0
pd.total_cost += cost
total_metrics.total_cost += cost
subs_count = subscriptions_counts.get(placement.id, 0)
subs_count = subscriptions_counts.get(publication.id, 0)
pd.total_subscriptions += subs_count
pd.clicks_count += subs_count
total_metrics.total_subscriptions += subs_count
total_metrics.clicks_count += subs_count
views_count = 0
if placement.post and placement.post.id in views_map:
views_count = views_map[placement.post.id][0]
if publication.post and publication.post.id in views_map:
views_count = views_map[publication.post.id][0]
pd.total_views += views_count
pd.reach_volume += views_count
total_metrics.total_views += views_count
total_metrics.reach_volume += views_count
# Расчет скидок
purchase_channel = placement.purchase_channel
if purchase_channel and purchase_channel.cost_before_bargain and purchase_channel.cost_before_bargain > cost:
discount = purchase_channel.cost_before_bargain - cost
discount_percent = (discount / purchase_channel.cost_before_bargain) * 100 if purchase_channel.cost_before_bargain > 0 else 0.0
placement = publication.placement
if placement and placement.cost_before_bargain and placement.cost_before_bargain > cost:
discount = placement.cost_before_bargain - cost
discount_percent = (discount / placement.cost_before_bargain) * 100 if placement.cost_before_bargain > 0 else 0.0
pd.total_discounts += discount
pd.discount_count += 1