refactor
This commit is contained in:
@@ -418,6 +418,7 @@ class Postgres(DatabaseBase):
|
|||||||
project_id: uuid.UUID | None = None,
|
project_id: uuid.UUID | None = None,
|
||||||
placement_channel_id: uuid.UUID | None = None,
|
placement_channel_id: uuid.UUID | None = None,
|
||||||
creative_id: uuid.UUID | None = None,
|
creative_id: uuid.UUID | None = None,
|
||||||
|
placement_id: uuid.UUID | None = None,
|
||||||
include_archived: bool = False,
|
include_archived: bool = False,
|
||||||
allowed_project_ids: set[uuid.UUID] | None = None,
|
allowed_project_ids: set[uuid.UUID] | None = None,
|
||||||
date_from: datetime.datetime | None = None,
|
date_from: datetime.datetime | None = None,
|
||||||
@@ -435,6 +436,8 @@ class Postgres(DatabaseBase):
|
|||||||
query = query.filter(placement__channel_id=placement_channel_id)
|
query = query.filter(placement__channel_id=placement_channel_id)
|
||||||
if creative_id:
|
if creative_id:
|
||||||
query = query.filter(placement__creative_id=creative_id)
|
query = query.filter(placement__creative_id=creative_id)
|
||||||
|
if placement_id:
|
||||||
|
query = query.filter(placement_id=placement_id)
|
||||||
|
|
||||||
if date_from:
|
if date_from:
|
||||||
query = query.filter(created_at__gte=date_from)
|
query = query.filter(created_at__gte=date_from)
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ async def list_placement_posts(
|
|||||||
project_id: uuid.UUID | None = None,
|
project_id: uuid.UUID | None = None,
|
||||||
placement_channel_id: uuid.UUID | None = None,
|
placement_channel_id: uuid.UUID | None = None,
|
||||||
creative_id: uuid.UUID | None = None,
|
creative_id: uuid.UUID | None = None,
|
||||||
|
placement_id: uuid.UUID | None = None,
|
||||||
include_archived: bool = False,
|
include_archived: bool = False,
|
||||||
) -> dto.GetPlacementPostsOutput:
|
) -> dto.GetPlacementPostsOutput:
|
||||||
"""List all placement_posts (system-managed actual posts) in workspace"""
|
"""List all placement_posts (system-managed actual posts) in workspace"""
|
||||||
@@ -27,6 +28,7 @@ async def list_placement_posts(
|
|||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
placement_channel_id=placement_channel_id,
|
placement_channel_id=placement_channel_id,
|
||||||
creative_id=creative_id,
|
creative_id=creative_id,
|
||||||
|
placement_id=placement_id,
|
||||||
include_archived=include_archived,
|
include_archived=include_archived,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import uuid
|
|||||||
import pydantic
|
import pydantic
|
||||||
|
|
||||||
from src import domain
|
from src import domain
|
||||||
|
from src.dto.purchase import PlacementOutput
|
||||||
|
|
||||||
|
|
||||||
class PlacementPostOutput(pydantic.BaseModel):
|
class PlacementPostOutput(pydantic.BaseModel):
|
||||||
@@ -23,6 +24,7 @@ class PlacementPostOutput(pydantic.BaseModel):
|
|||||||
status: domain.PlacementPostStatus
|
status: domain.PlacementPostStatus
|
||||||
subscriptions_count: int
|
subscriptions_count: int
|
||||||
placement_id: uuid.UUID # Ссылка на Placement
|
placement_id: uuid.UUID # Ссылка на Placement
|
||||||
|
placement: PlacementOutput
|
||||||
created_at: datetime.datetime
|
created_at: datetime.datetime
|
||||||
|
|
||||||
|
|
||||||
@@ -32,6 +34,7 @@ class GetPlacementPostsInput(pydantic.BaseModel):
|
|||||||
project_id: uuid.UUID | None = None
|
project_id: uuid.UUID | None = None
|
||||||
placement_channel_id: uuid.UUID | None = None
|
placement_channel_id: uuid.UUID | None = None
|
||||||
creative_id: uuid.UUID | None = None
|
creative_id: uuid.UUID | None = None
|
||||||
|
placement_id: uuid.UUID | None = None
|
||||||
include_archived: bool = False
|
include_archived: bool = False
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,13 @@ async def fetch_placement_post_cycle(self: 'Usecase', interval_seconds: int) ->
|
|||||||
|
|
||||||
# Получаем все approved Placements с invite_link
|
# Получаем все approved Placements с invite_link
|
||||||
placements = (
|
placements = (
|
||||||
await domain.Placement.filter(status__in=[domain.PlacementStatus.APPROVED, domain.PlacementStatus.IN_PROGRESS])
|
await domain.Placement.filter(
|
||||||
|
status__in=[
|
||||||
|
domain.PlacementStatus.APPROVED,
|
||||||
|
domain.PlacementStatus.PLANNED,
|
||||||
|
domain.PlacementStatus.IN_PROGRESS,
|
||||||
|
]
|
||||||
|
)
|
||||||
.prefetch_related('channel', 'project')
|
.prefetch_related('channel', 'project')
|
||||||
.all()
|
.all()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import logging
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from src import domain, dto
|
from src import domain, dto
|
||||||
|
from src.usecase.purchase.create_placements import _build_placement_output
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .. import Usecase
|
from .. import Usecase
|
||||||
@@ -51,5 +52,6 @@ async def get_placement_post(self: 'Usecase', input: dto.GetPlacementPostInput)
|
|||||||
status=placement_post.status,
|
status=placement_post.status,
|
||||||
subscriptions_count=subscriptions_count,
|
subscriptions_count=subscriptions_count,
|
||||||
placement_id=placement.id,
|
placement_id=placement.id,
|
||||||
|
placement=_build_placement_output(placement),
|
||||||
created_at=placement_post.created_at,
|
created_at=placement_post.created_at,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import logging
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from src import domain, dto
|
from src import domain, dto
|
||||||
|
from src.usecase.purchase.create_placements import _build_placement_output
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .. import Usecase
|
from .. import Usecase
|
||||||
@@ -26,6 +27,7 @@ async def get_placement_posts(self: 'Usecase', input: dto.GetPlacementPostsInput
|
|||||||
project_id=input.project_id,
|
project_id=input.project_id,
|
||||||
placement_channel_id=input.placement_channel_id,
|
placement_channel_id=input.placement_channel_id,
|
||||||
creative_id=input.creative_id,
|
creative_id=input.creative_id,
|
||||||
|
placement_id=input.placement_id,
|
||||||
include_archived=input.include_archived,
|
include_archived=input.include_archived,
|
||||||
allowed_project_ids=allowed_project_ids,
|
allowed_project_ids=allowed_project_ids,
|
||||||
)
|
)
|
||||||
@@ -64,6 +66,7 @@ async def get_placement_posts(self: 'Usecase', input: dto.GetPlacementPostsInput
|
|||||||
status=placement_post.status,
|
status=placement_post.status,
|
||||||
subscriptions_count=subscriptions_counts.get(placement_post.id, 0),
|
subscriptions_count=subscriptions_counts.get(placement_post.id, 0),
|
||||||
placement_id=placement.id,
|
placement_id=placement.id,
|
||||||
|
placement=_build_placement_output(placement),
|
||||||
created_at=placement_post.created_at,
|
created_at=placement_post.created_at,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -567,6 +567,16 @@ type PlacementOutput struct {
|
|||||||
Details *PlacementDetails `json:"details,omitempty"`
|
Details *PlacementDetails `json:"details,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PlacementPostOutput struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
PlacementID string `json:"placement_id"`
|
||||||
|
Placement PlacementOutput `json:"placement"`
|
||||||
|
AdPostURL *string `json:"ad_post_url,omitempty"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
SubscriptionsCount int `json:"subscriptions_count"`
|
||||||
|
CreatedAt string `json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
type CreatePlacementChannelInput struct {
|
type CreatePlacementChannelInput struct {
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Status *string `json:"status,omitempty"`
|
Status *string `json:"status,omitempty"`
|
||||||
@@ -584,6 +594,10 @@ type GetPlacementsOutput struct {
|
|||||||
Placements []PlacementOutput `json:"placements"`
|
Placements []PlacementOutput `json:"placements"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetPlacementPostsOutput struct {
|
||||||
|
PlacementPosts []PlacementPostOutput `json:"placement_posts"`
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) CreatePlacements(
|
func (c *Client) CreatePlacements(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
jwt string,
|
jwt string,
|
||||||
@@ -650,6 +664,39 @@ func (c *Client) GetPlacement(
|
|||||||
return &placement, err
|
return &placement, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) GetPlacementPosts(
|
||||||
|
ctx context.Context,
|
||||||
|
jwt string,
|
||||||
|
workspaceID string,
|
||||||
|
projectID *string,
|
||||||
|
placementID *string,
|
||||||
|
) (*GetPlacementPostsOutput, error) {
|
||||||
|
q := url.Values{}
|
||||||
|
if projectID != nil && *projectID != "" {
|
||||||
|
q.Set("project_id", *projectID)
|
||||||
|
}
|
||||||
|
if placementID != nil && *placementID != "" {
|
||||||
|
q.Set("placement_id", *placementID)
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("api/v1/workspaces/%s/placement_posts", workspaceID)
|
||||||
|
if len(q) > 0 {
|
||||||
|
path += "?" + q.Encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp GetPlacementPostsOutput
|
||||||
|
err := c.do(
|
||||||
|
ctx,
|
||||||
|
http.MethodGet,
|
||||||
|
path,
|
||||||
|
nil,
|
||||||
|
&resp,
|
||||||
|
withBearer(jwt),
|
||||||
|
)
|
||||||
|
|
||||||
|
return &resp, err
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Workspace Members
|
// Workspace Members
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
@@ -44,6 +44,28 @@ func (s *PlacementDetails) renderDetails(b *bot.Bot, mode bot.RenderMode, jwt st
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
placementPosts, err := b.Backend.GetPlacementPosts(
|
||||||
|
context.Background(),
|
||||||
|
jwt,
|
||||||
|
s.WorkspaceID,
|
||||||
|
&s.ProjectID,
|
||||||
|
&s.PlacementID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Failed to get placement posts")
|
||||||
|
}
|
||||||
|
|
||||||
|
var placementPost *backend.PlacementPostOutput
|
||||||
|
if placementPosts != nil {
|
||||||
|
for i := range placementPosts.PlacementPosts {
|
||||||
|
post := &placementPosts.PlacementPosts[i]
|
||||||
|
if post.PlacementID == s.PlacementID {
|
||||||
|
placementPost = post
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
text := "<b>Детали размещения</b>\n\n"
|
text := "<b>Детали размещения</b>\n\n"
|
||||||
|
|
||||||
channelName := formatChannelName(placement.Channel)
|
channelName := formatChannelName(placement.Channel)
|
||||||
@@ -78,6 +100,16 @@ func (s *PlacementDetails) renderDetails(b *bot.Bot, mode bot.RenderMode, jwt st
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if placementPost != nil {
|
||||||
|
text += "\n<b>Пост</b>\n"
|
||||||
|
if placementPost.AdPostURL != nil && *placementPost.AdPostURL != "" {
|
||||||
|
text += fmt.Sprintf("• <b>Ссылка на пост:</b> %s\n", *placementPost.AdPostURL)
|
||||||
|
} else {
|
||||||
|
text += "• <b>Ссылка на пост:</b> не найдена\n"
|
||||||
|
}
|
||||||
|
text += fmt.Sprintf("• <b>Статус поста:</b> %s\n", formatPlacementPostStatus(placementPost.Status))
|
||||||
|
}
|
||||||
|
|
||||||
keyboard := Keyboard(Row(Button("← Назад", "back")))
|
keyboard := Keyboard(Row(Button("← Назад", "back")))
|
||||||
b.Render(text, keyboard, mode)
|
b.Render(text, keyboard, mode)
|
||||||
}
|
}
|
||||||
@@ -129,6 +161,18 @@ func formatPlacementType(value string) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func formatPlacementPostStatus(status string) string {
|
||||||
|
normalized := strings.TrimSpace(strings.ToLower(status))
|
||||||
|
switch normalized {
|
||||||
|
case "active":
|
||||||
|
return "Активен"
|
||||||
|
case "archived":
|
||||||
|
return "Архивирован"
|
||||||
|
default:
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func formatCostType(value string) string {
|
func formatCostType(value string) string {
|
||||||
switch strings.TrimSpace(strings.ToLower(value)) {
|
switch strings.TrimSpace(strings.ToLower(value)) {
|
||||||
case "cpm":
|
case "cpm":
|
||||||
|
|||||||
Reference in New Issue
Block a user