домен обновлен
This commit is contained in:
@@ -1,335 +0,0 @@
|
|||||||
from tortoise import BaseDBAsyncClient
|
|
||||||
|
|
||||||
RUN_IN_TRANSACTION = True
|
|
||||||
|
|
||||||
|
|
||||||
async def upgrade(db: BaseDBAsyncClient) -> str:
|
|
||||||
return """
|
|
||||||
CREATE TABLE IF NOT EXISTS "channel"
|
|
||||||
(
|
|
||||||
"id" UUID NOT NULL PRIMARY KEY,
|
|
||||||
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"deleted_at" TIMESTAMPTZ,
|
|
||||||
"telegram_id" BIGINT NOT NULL UNIQUE,
|
|
||||||
"title" VARCHAR(255) NOT NULL,
|
|
||||||
"username" VARCHAR(255) NOT NULL UNIQUE,
|
|
||||||
"access_hash" BIGINT,
|
|
||||||
"pts" INT
|
|
||||||
);
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_channel_telegra_414c3b" ON "channel" ("telegram_id");
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_channel_usernam_7fd87a" ON "channel" ("username");
|
|
||||||
CREATE TABLE IF NOT EXISTS "post"
|
|
||||||
(
|
|
||||||
"id" UUID NOT NULL PRIMARY KEY,
|
|
||||||
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"deleted_at" TIMESTAMPTZ,
|
|
||||||
"message_id" INT NOT NULL,
|
|
||||||
"text" TEXT NOT NULL,
|
|
||||||
"deleted_from_channel_at" TIMESTAMPTZ,
|
|
||||||
"channel_id" UUID NOT NULL REFERENCES "channel" ("id") ON DELETE CASCADE,
|
|
||||||
CONSTRAINT "uid_post_channel_0a2cba" UNIQUE ("channel_id", "message_id")
|
|
||||||
);
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_post_channel_197bed" ON "post" ("channel_id");
|
|
||||||
CREATE TABLE IF NOT EXISTS "post_views_history"
|
|
||||||
(
|
|
||||||
"id" UUID NOT NULL PRIMARY KEY,
|
|
||||||
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"deleted_at" TIMESTAMPTZ,
|
|
||||||
"views_count" INT NOT NULL,
|
|
||||||
"fetched_at" TIMESTAMPTZ NOT NULL,
|
|
||||||
"post_id" UUID NOT NULL REFERENCES "post" ("id") ON DELETE CASCADE,
|
|
||||||
CONSTRAINT "uid_post_views__post_id_c6cc9f" UNIQUE ("post_id", "fetched_at")
|
|
||||||
);
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_post_views__fetched_103eb5" ON "post_views_history" ("fetched_at");
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_post_views__post_id_dfb1ca" ON "post_views_history" ("post_id");
|
|
||||||
CREATE TABLE IF NOT EXISTS "user"
|
|
||||||
(
|
|
||||||
"id" UUID NOT NULL PRIMARY KEY,
|
|
||||||
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"deleted_at" TIMESTAMPTZ,
|
|
||||||
"telegram_id" BIGINT NOT NULL UNIQUE,
|
|
||||||
"username" VARCHAR(255),
|
|
||||||
"first_name" VARCHAR(255),
|
|
||||||
"last_name" VARCHAR(255)
|
|
||||||
);
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_user_telegra_66ffbd" ON "user" ("telegram_id");
|
|
||||||
CREATE TABLE IF NOT EXISTS "login_token"
|
|
||||||
(
|
|
||||||
"id" UUID NOT NULL PRIMARY KEY,
|
|
||||||
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"deleted_at" TIMESTAMPTZ,
|
|
||||||
"token" VARCHAR(255) NOT NULL UNIQUE,
|
|
||||||
"expires_at" TIMESTAMPTZ NOT NULL,
|
|
||||||
"used_at" TIMESTAMPTZ,
|
|
||||||
"user_id" UUID NOT NULL REFERENCES "user" ("id") ON DELETE CASCADE
|
|
||||||
);
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_login_token_token_2582d2" ON "login_token" ("token");
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_login_token_user_id_c85d7c" ON "login_token" ("user_id");
|
|
||||||
CREATE TABLE IF NOT EXISTS "workspace"
|
|
||||||
(
|
|
||||||
"id" UUID NOT NULL PRIMARY KEY,
|
|
||||||
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"deleted_at" TIMESTAMPTZ,
|
|
||||||
"name" VARCHAR(255) NOT NULL
|
|
||||||
);
|
|
||||||
CREATE TABLE IF NOT EXISTS "project"
|
|
||||||
(
|
|
||||||
"id" UUID NOT NULL PRIMARY KEY,
|
|
||||||
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"deleted_at" TIMESTAMPTZ,
|
|
||||||
"status" VARCHAR(8) NOT NULL DEFAULT 'active',
|
|
||||||
"purchase_invite_type_default" VARCHAR(10) NOT NULL DEFAULT 'approval',
|
|
||||||
"channel_id" UUID NOT NULL REFERENCES "channel" ("id") ON DELETE CASCADE,
|
|
||||||
"workspace_id" UUID NOT NULL REFERENCES "workspace" ("id") ON DELETE CASCADE,
|
|
||||||
CONSTRAINT "uid_project_workspa_2f80f2" UNIQUE ("workspace_id", "channel_id")
|
|
||||||
);
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_project_channel_ce2466" ON "project" ("channel_id");
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_project_workspa_2354fd" ON "project" ("workspace_id");
|
|
||||||
COMMENT ON COLUMN "project"."status" IS 'ACTIVE: active\nINACTIVE: inactive\nARCHIVED: archived';
|
|
||||||
COMMENT ON COLUMN "project"."purchase_invite_type_default" IS 'PUBLIC: public\nAPPROVAL: approval';
|
|
||||||
CREATE TABLE IF NOT EXISTS "creative"
|
|
||||||
(
|
|
||||||
"id" UUID NOT NULL PRIMARY KEY,
|
|
||||||
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"deleted_at" TIMESTAMPTZ,
|
|
||||||
"name" VARCHAR(255) NOT NULL,
|
|
||||||
"text" TEXT NOT NULL,
|
|
||||||
"status" VARCHAR(8) NOT NULL DEFAULT 'active',
|
|
||||||
"project_id" UUID NOT NULL REFERENCES "project" ("id") ON DELETE CASCADE,
|
|
||||||
"workspace_id" UUID NOT NULL REFERENCES "workspace" ("id") ON DELETE CASCADE
|
|
||||||
);
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_creative_project_0766a6" ON "creative" ("project_id");
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_creative_workspa_2de546" ON "creative" ("workspace_id");
|
|
||||||
COMMENT ON COLUMN "creative"."status" IS 'ACTIVE: active\nARCHIVED: archived';
|
|
||||||
CREATE TABLE IF NOT EXISTS "purchase"
|
|
||||||
(
|
|
||||||
"id" UUID NOT NULL PRIMARY KEY,
|
|
||||||
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"deleted_at" TIMESTAMPTZ,
|
|
||||||
"status" VARCHAR(8) NOT NULL DEFAULT 'active',
|
|
||||||
"creative_id" UUID NOT NULL REFERENCES "creative" ("id") ON DELETE CASCADE,
|
|
||||||
"project_id" UUID NOT NULL REFERENCES "project" ("id") ON DELETE CASCADE,
|
|
||||||
"workspace_id" UUID NOT NULL REFERENCES "workspace" ("id") ON DELETE CASCADE
|
|
||||||
);
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_purchase_creativ_6829cd" ON "purchase" ("creative_id");
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_purchase_project_95b1c2" ON "purchase" ("project_id");
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_purchase_workspa_8784a5" ON "purchase" ("workspace_id");
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_purchase_project_dee224" ON "purchase" ("project_id", "status");
|
|
||||||
COMMENT ON COLUMN "purchase"."status" IS 'ACTIVE: active\nARCHIVED: archived';
|
|
||||||
CREATE TABLE IF NOT EXISTS "purchase_channel"
|
|
||||||
(
|
|
||||||
"id" UUID NOT NULL PRIMARY KEY,
|
|
||||||
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"deleted_at" TIMESTAMPTZ,
|
|
||||||
"status" VARCHAR(11) NOT NULL DEFAULT 'planned',
|
|
||||||
"planned_cost" DOUBLE PRECISION,
|
|
||||||
"comment" TEXT,
|
|
||||||
"invite_link" VARCHAR(512) NOT NULL,
|
|
||||||
"invite_link_type" VARCHAR(8) NOT NULL,
|
|
||||||
"channel_id" UUID NOT NULL REFERENCES "channel" ("id") ON DELETE CASCADE,
|
|
||||||
"purchase_id" UUID NOT NULL REFERENCES "purchase" ("id") ON DELETE CASCADE,
|
|
||||||
CONSTRAINT "uid_purchase_ch_purchas_f7c86e" UNIQUE ("purchase_id", "channel_id")
|
|
||||||
);
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_purchase_ch_channel_2b1264" ON "purchase_channel" ("channel_id");
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_purchase_ch_purchas_1fe84a" ON "purchase_channel" ("purchase_id");
|
|
||||||
COMMENT ON COLUMN "purchase_channel"."status" IS 'PLANNED: planned\nAPPROVED: approved\nREJECTED: rejected\nIN_PROGRESS: in_progress\nCOMPLETED: completed';
|
|
||||||
COMMENT ON COLUMN "purchase_channel"."invite_link_type" IS 'PUBLIC: public\nAPPROVAL: approval';
|
|
||||||
CREATE TABLE IF NOT EXISTS "placement"
|
|
||||||
(
|
|
||||||
"id" UUID NOT NULL PRIMARY KEY,
|
|
||||||
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"deleted_at" TIMESTAMPTZ,
|
|
||||||
"wanted_placement_date" TIMESTAMPTZ NOT NULL,
|
|
||||||
"cost" DOUBLE PRECISION,
|
|
||||||
"comment" TEXT,
|
|
||||||
"invite_link" VARCHAR(512) NOT NULL,
|
|
||||||
"status" VARCHAR(8) NOT NULL DEFAULT 'active',
|
|
||||||
"creative_id" UUID NOT NULL REFERENCES "creative" ("id") ON DELETE CASCADE,
|
|
||||||
"placement_channel_id" UUID NOT NULL REFERENCES "channel" ("id") ON DELETE CASCADE,
|
|
||||||
"post_id" UUID REFERENCES "post" ("id") ON DELETE SET NULL,
|
|
||||||
"project_id" UUID NOT NULL REFERENCES "project" ("id") ON DELETE CASCADE,
|
|
||||||
"purchase_id" UUID REFERENCES "purchase" ("id") ON DELETE CASCADE,
|
|
||||||
"purchase_channel_id" UUID REFERENCES "purchase_channel" ("id") ON DELETE CASCADE,
|
|
||||||
"workspace_id" UUID NOT NULL REFERENCES "workspace" ("id") ON DELETE CASCADE
|
|
||||||
);
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_placement_creativ_32111d" ON "placement" ("creative_id");
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_placement_placeme_20cf53" ON "placement" ("placement_channel_id");
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_placement_post_id_e4a16e" ON "placement" ("post_id");
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_placement_project_2c6aa4" ON "placement" ("project_id");
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_placement_purchas_3e3775" ON "placement" ("purchase_id");
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_placement_purchas_61f9e6" ON "placement" ("purchase_channel_id");
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_placement_workspa_632b20" ON "placement" ("workspace_id");
|
|
||||||
COMMENT ON COLUMN "placement"."status" IS 'ACTIVE: active\nARCHIVED: archived';
|
|
||||||
CREATE TABLE IF NOT EXISTS "subscription"
|
|
||||||
(
|
|
||||||
"id" UUID NOT NULL PRIMARY KEY,
|
|
||||||
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"deleted_at" TIMESTAMPTZ,
|
|
||||||
"invite_link" VARCHAR(512) NOT NULL,
|
|
||||||
"status" VARCHAR(12) NOT NULL DEFAULT 'active',
|
|
||||||
"unsubscribed_at" TIMESTAMPTZ,
|
|
||||||
"placement_id" UUID NOT NULL REFERENCES "placement" ("id") ON DELETE CASCADE,
|
|
||||||
"subscriber_id" UUID NOT NULL REFERENCES "user" ("id") ON DELETE CASCADE,
|
|
||||||
CONSTRAINT "uid_subscriptio_placeme_8fcb8f" UNIQUE ("placement_id", "subscriber_id")
|
|
||||||
);
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_subscriptio_invite__f2058d" ON "subscription" ("invite_link");
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_subscriptio_placeme_5decb2" ON "subscription" ("placement_id");
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_subscriptio_subscri_e75729" ON "subscription" ("subscriber_id");
|
|
||||||
COMMENT ON COLUMN "subscription"."status" IS 'ACTIVE: active\nUNSUBSCRIBED: unsubscribed';
|
|
||||||
CREATE TABLE IF NOT EXISTS "workspace_invite"
|
|
||||||
(
|
|
||||||
"id" UUID NOT NULL PRIMARY KEY,
|
|
||||||
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"deleted_at" TIMESTAMPTZ,
|
|
||||||
"status" VARCHAR(8) NOT NULL DEFAULT 'pending',
|
|
||||||
"invited_by_id" UUID NOT NULL REFERENCES "user" ("id") ON DELETE CASCADE,
|
|
||||||
"user_id" UUID NOT NULL REFERENCES "user" ("id") ON DELETE CASCADE,
|
|
||||||
"workspace_id" UUID NOT NULL REFERENCES "workspace" ("id") ON DELETE CASCADE,
|
|
||||||
CONSTRAINT "uid_workspace_i_workspa_abd9c4" UNIQUE ("workspace_id", "user_id")
|
|
||||||
);
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_workspace_i_invited_3ed9f8" ON "workspace_invite" ("invited_by_id");
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_workspace_i_user_id_a96e1e" ON "workspace_invite" ("user_id");
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_workspace_i_workspa_71e9d2" ON "workspace_invite" ("workspace_id");
|
|
||||||
COMMENT ON COLUMN "workspace_invite"."status" IS 'PENDING: pending\nACCEPTED: accepted\nREVOKED: revoked';
|
|
||||||
CREATE TABLE IF NOT EXISTS "workspace_user"
|
|
||||||
(
|
|
||||||
"id" UUID NOT NULL PRIMARY KEY,
|
|
||||||
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"deleted_at" TIMESTAMPTZ,
|
|
||||||
"status" VARCHAR(7) NOT NULL DEFAULT 'active',
|
|
||||||
"user_id" UUID NOT NULL REFERENCES "user" ("id") ON DELETE CASCADE,
|
|
||||||
"workspace_id" UUID NOT NULL REFERENCES "workspace" ("id") ON DELETE CASCADE,
|
|
||||||
CONSTRAINT "uid_workspace_u_workspa_13c46c" UNIQUE ("workspace_id", "user_id")
|
|
||||||
);
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_workspace_u_user_id_246318" ON "workspace_user" ("user_id");
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_workspace_u_workspa_a403b0" ON "workspace_user" ("workspace_id");
|
|
||||||
COMMENT ON COLUMN "workspace_user"."status" IS 'ACTIVE: active\nINVITED: invited\nBLOCKED: blocked';
|
|
||||||
CREATE TABLE IF NOT EXISTS "workspace_user_permission"
|
|
||||||
(
|
|
||||||
"id" UUID NOT NULL PRIMARY KEY,
|
|
||||||
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"deleted_at" TIMESTAMPTZ,
|
|
||||||
"permission" VARCHAR(24) NOT NULL,
|
|
||||||
"workspace_user_id" UUID NOT NULL REFERENCES "workspace_user" ("id") ON DELETE CASCADE,
|
|
||||||
CONSTRAINT "uid_workspace_u_workspa_86f574" UNIQUE ("workspace_user_id", "permission")
|
|
||||||
);
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_workspace_u_workspa_b9b7b0" ON "workspace_user_permission" ("workspace_user_id");
|
|
||||||
COMMENT ON COLUMN "workspace_user_permission"."permission" IS 'ADMIN_FULL: admin_full\nPROJECTS_READ: projects_read\nPROJECTS_WRITE: projects_write\nCREATIVES_READ: creatives_read\nCREATIVES_WRITE: creatives_write\nPLACEMENTS_READ: placements_read\nPLACEMENTS_WRITE: placements_write\nANALYTICS_READ: analytics_read\nANALYTICS_WITHOUT_CLICKS: analytics_without_clicks\nCHANNELS_READ: channels_read\nCHANNELS_WRITE: channels_write';
|
|
||||||
CREATE TABLE IF NOT EXISTS "workspace_user_permission_scope"
|
|
||||||
(
|
|
||||||
"id" UUID NOT NULL PRIMARY KEY,
|
|
||||||
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"deleted_at" TIMESTAMPTZ,
|
|
||||||
"permission" VARCHAR(24) NOT NULL,
|
|
||||||
"channel_id" UUID REFERENCES "channel" ("id") ON DELETE CASCADE,
|
|
||||||
"creative_id" UUID REFERENCES "creative" ("id") ON DELETE CASCADE,
|
|
||||||
"placement_id" UUID REFERENCES "placement" ("id") ON DELETE CASCADE,
|
|
||||||
"project_id" UUID REFERENCES "project" ("id") ON DELETE CASCADE,
|
|
||||||
"workspace_user_id" UUID NOT NULL REFERENCES "workspace_user" ("id") ON DELETE CASCADE
|
|
||||||
);
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_workspace_u_channel_d94b48" ON "workspace_user_permission_scope" ("channel_id");
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_workspace_u_creativ_8da562" ON "workspace_user_permission_scope" ("creative_id");
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_workspace_u_placeme_ea1846" ON "workspace_user_permission_scope" ("placement_id");
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_workspace_u_project_bd7fea" ON "workspace_user_permission_scope" ("project_id");
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_workspace_u_workspa_82a35a" ON "workspace_user_permission_scope" ("workspace_user_id");
|
|
||||||
COMMENT ON COLUMN "workspace_user_permission_scope"."permission" IS 'ADMIN_FULL: admin_full\nPROJECTS_READ: projects_read\nPROJECTS_WRITE: projects_write\nCREATIVES_READ: creatives_read\nCREATIVES_WRITE: creatives_write\nPLACEMENTS_READ: placements_read\nPLACEMENTS_WRITE: placements_write\nANALYTICS_READ: analytics_read\nANALYTICS_WITHOUT_CLICKS: analytics_without_clicks\nCHANNELS_READ: channels_read\nCHANNELS_WRITE: channels_write';
|
|
||||||
CREATE TABLE IF NOT EXISTS "aerich"
|
|
||||||
(
|
|
||||||
"id" SERIAL NOT NULL PRIMARY KEY,
|
|
||||||
"version" VARCHAR(255) NOT NULL,
|
|
||||||
"app" VARCHAR(100) NOT NULL,
|
|
||||||
"content" JSONB NOT NULL
|
|
||||||
);"""
|
|
||||||
|
|
||||||
|
|
||||||
async def downgrade(db: BaseDBAsyncClient) -> str:
|
|
||||||
return """
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
MODELS_STATE = (
|
|
||||||
'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'
|
|
||||||
)
|
|
||||||
342
migrations/models/0_20260107150827_init.py
Normal file
342
migrations/models/0_20260107150827_init.py
Normal file
@@ -0,0 +1,342 @@
|
|||||||
|
from tortoise import BaseDBAsyncClient
|
||||||
|
|
||||||
|
RUN_IN_TRANSACTION = True
|
||||||
|
|
||||||
|
|
||||||
|
async def upgrade(db: BaseDBAsyncClient) -> str:
|
||||||
|
return """
|
||||||
|
CREATE TABLE IF NOT EXISTS "channel" (
|
||||||
|
"id" UUID NOT NULL PRIMARY KEY,
|
||||||
|
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"deleted_at" TIMESTAMPTZ,
|
||||||
|
"telegram_id" BIGINT NOT NULL UNIQUE,
|
||||||
|
"title" VARCHAR(255) NOT NULL,
|
||||||
|
"username" VARCHAR(255) NOT NULL UNIQUE,
|
||||||
|
"access_hash" BIGINT,
|
||||||
|
"pts" INT
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_channel_telegra_414c3b" ON "channel" ("telegram_id");
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_channel_usernam_7fd87a" ON "channel" ("username");
|
||||||
|
CREATE TABLE IF NOT EXISTS "post" (
|
||||||
|
"id" UUID NOT NULL PRIMARY KEY,
|
||||||
|
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"deleted_at" TIMESTAMPTZ,
|
||||||
|
"message_id" INT NOT NULL,
|
||||||
|
"text" TEXT NOT NULL,
|
||||||
|
"deleted_from_channel_at" TIMESTAMPTZ,
|
||||||
|
"channel_id" UUID NOT NULL REFERENCES "channel" ("id") ON DELETE CASCADE,
|
||||||
|
CONSTRAINT "uid_post_channel_0a2cba" UNIQUE ("channel_id", "message_id")
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_post_channel_197bed" ON "post" ("channel_id");
|
||||||
|
CREATE TABLE IF NOT EXISTS "post_views_history" (
|
||||||
|
"id" UUID NOT NULL PRIMARY KEY,
|
||||||
|
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"deleted_at" TIMESTAMPTZ,
|
||||||
|
"views_count" INT NOT NULL,
|
||||||
|
"fetched_at" TIMESTAMPTZ NOT NULL,
|
||||||
|
"post_id" UUID NOT NULL REFERENCES "post" ("id") ON DELETE CASCADE,
|
||||||
|
CONSTRAINT "uid_post_views__post_id_c6cc9f" UNIQUE ("post_id", "fetched_at")
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_post_views__fetched_103eb5" ON "post_views_history" ("fetched_at");
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_post_views__post_id_dfb1ca" ON "post_views_history" ("post_id");
|
||||||
|
CREATE TABLE IF NOT EXISTS "telegram_user" (
|
||||||
|
"id" UUID NOT NULL PRIMARY KEY,
|
||||||
|
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"deleted_at" TIMESTAMPTZ,
|
||||||
|
"telegram_id" BIGINT NOT NULL UNIQUE,
|
||||||
|
"username" VARCHAR(255),
|
||||||
|
"first_name" VARCHAR(255),
|
||||||
|
"last_name" VARCHAR(255)
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_telegram_us_telegra_3a3100" ON "telegram_user" ("telegram_id");
|
||||||
|
CREATE TABLE IF NOT EXISTS "user" (
|
||||||
|
"id" UUID NOT NULL PRIMARY KEY,
|
||||||
|
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"deleted_at" TIMESTAMPTZ,
|
||||||
|
"telegram_user_id" UUID NOT NULL UNIQUE REFERENCES "telegram_user" ("id") ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_user_telegra_444a03" ON "user" ("telegram_user_id");
|
||||||
|
CREATE TABLE IF NOT EXISTS "login_token" (
|
||||||
|
"id" UUID NOT NULL PRIMARY KEY,
|
||||||
|
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"deleted_at" TIMESTAMPTZ,
|
||||||
|
"token" VARCHAR(255) NOT NULL UNIQUE,
|
||||||
|
"expires_at" TIMESTAMPTZ NOT NULL,
|
||||||
|
"used_at" TIMESTAMPTZ,
|
||||||
|
"user_id" UUID NOT NULL REFERENCES "user" ("id") ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_login_token_token_2582d2" ON "login_token" ("token");
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_login_token_user_id_c85d7c" ON "login_token" ("user_id");
|
||||||
|
CREATE TABLE IF NOT EXISTS "workspace" (
|
||||||
|
"id" UUID NOT NULL PRIMARY KEY,
|
||||||
|
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"deleted_at" TIMESTAMPTZ,
|
||||||
|
"name" VARCHAR(255) NOT NULL
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS "project" (
|
||||||
|
"id" UUID NOT NULL PRIMARY KEY,
|
||||||
|
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"deleted_at" TIMESTAMPTZ,
|
||||||
|
"status" VARCHAR(8) NOT NULL DEFAULT 'active',
|
||||||
|
"purchase_invite_type_default" VARCHAR(10) NOT NULL DEFAULT 'approval',
|
||||||
|
"channel_id" UUID NOT NULL REFERENCES "channel" ("id") ON DELETE CASCADE,
|
||||||
|
"workspace_id" UUID NOT NULL REFERENCES "workspace" ("id") ON DELETE CASCADE,
|
||||||
|
CONSTRAINT "uid_project_workspa_2f80f2" UNIQUE ("workspace_id", "channel_id")
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_project_channel_ce2466" ON "project" ("channel_id");
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_project_workspa_2354fd" ON "project" ("workspace_id");
|
||||||
|
COMMENT ON COLUMN "project"."status" IS 'ACTIVE: active\nINACTIVE: inactive\nARCHIVED: archived';
|
||||||
|
COMMENT ON COLUMN "project"."purchase_invite_type_default" IS 'PUBLIC: public\nAPPROVAL: approval';
|
||||||
|
CREATE TABLE IF NOT EXISTS "creative" (
|
||||||
|
"id" UUID NOT NULL PRIMARY KEY,
|
||||||
|
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"deleted_at" TIMESTAMPTZ,
|
||||||
|
"name" VARCHAR(255) NOT NULL,
|
||||||
|
"text" TEXT NOT NULL,
|
||||||
|
"media_type" VARCHAR(32),
|
||||||
|
"media_file_id" VARCHAR(512),
|
||||||
|
"media_s3_key" VARCHAR(512),
|
||||||
|
"buttons" JSONB NOT NULL,
|
||||||
|
"status" VARCHAR(8) NOT NULL DEFAULT 'active',
|
||||||
|
"project_id" UUID NOT NULL REFERENCES "project" ("id") ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_creative_project_0766a6" ON "creative" ("project_id");
|
||||||
|
COMMENT ON COLUMN "creative"."status" IS 'ACTIVE: active\nARCHIVED: archived';
|
||||||
|
CREATE TABLE IF NOT EXISTS "purchase" (
|
||||||
|
"id" UUID NOT NULL PRIMARY KEY,
|
||||||
|
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"deleted_at" TIMESTAMPTZ,
|
||||||
|
"status" VARCHAR(8) NOT NULL DEFAULT 'active',
|
||||||
|
"placement_at" TIMESTAMPTZ,
|
||||||
|
"payment_at" TIMESTAMPTZ,
|
||||||
|
"cost_type" VARCHAR(8),
|
||||||
|
"cost_value" DOUBLE PRECISION,
|
||||||
|
"cost_before_bargain" DOUBLE PRECISION,
|
||||||
|
"purchase_type" VARCHAR(16),
|
||||||
|
"format" TEXT,
|
||||||
|
"comment" TEXT,
|
||||||
|
"creative_id" UUID NOT NULL REFERENCES "creative" ("id") ON DELETE CASCADE,
|
||||||
|
"project_id" UUID NOT NULL REFERENCES "project" ("id") ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_purchase_creativ_6829cd" ON "purchase" ("creative_id");
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_purchase_project_95b1c2" ON "purchase" ("project_id");
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_purchase_project_dee224" ON "purchase" ("project_id", "status");
|
||||||
|
COMMENT ON COLUMN "purchase"."status" IS 'ACTIVE: active\nARCHIVED: archived';
|
||||||
|
COMMENT ON COLUMN "purchase"."cost_type" IS 'FIXED: fixed\nCPM: cpm';
|
||||||
|
COMMENT ON COLUMN "purchase"."purchase_type" IS 'SELF_PROMO: self_promo\nSTANDARD: standard';
|
||||||
|
CREATE TABLE IF NOT EXISTS "purchase_channel" (
|
||||||
|
"id" UUID NOT NULL PRIMARY KEY,
|
||||||
|
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"deleted_at" TIMESTAMPTZ,
|
||||||
|
"status" VARCHAR(11) NOT NULL DEFAULT 'planned',
|
||||||
|
"placement_at" TIMESTAMPTZ,
|
||||||
|
"cost_type" VARCHAR(8),
|
||||||
|
"cost_value" DOUBLE PRECISION,
|
||||||
|
"cost_before_bargain" DOUBLE PRECISION,
|
||||||
|
"format" TEXT,
|
||||||
|
"comment" TEXT,
|
||||||
|
"invite_link" VARCHAR(512) NOT NULL,
|
||||||
|
"invite_link_type" VARCHAR(8) NOT NULL,
|
||||||
|
"channel_id" UUID NOT NULL REFERENCES "channel" ("id") ON DELETE CASCADE,
|
||||||
|
"purchase_id" UUID NOT NULL REFERENCES "purchase" ("id") ON DELETE CASCADE,
|
||||||
|
CONSTRAINT "uid_purchase_ch_purchas_f7c86e" UNIQUE ("purchase_id", "channel_id")
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_purchase_ch_channel_2b1264" ON "purchase_channel" ("channel_id");
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_purchase_ch_purchas_1fe84a" ON "purchase_channel" ("purchase_id");
|
||||||
|
COMMENT ON COLUMN "purchase_channel"."status" IS 'PLANNED: planned\nAPPROVED: approved\nREJECTED: rejected\nIN_PROGRESS: in_progress\nCOMPLETED: completed';
|
||||||
|
COMMENT ON COLUMN "purchase_channel"."cost_type" IS 'FIXED: fixed\nCPM: cpm';
|
||||||
|
COMMENT ON COLUMN "purchase_channel"."invite_link_type" IS 'PUBLIC: public\nAPPROVAL: approval';
|
||||||
|
CREATE TABLE IF NOT EXISTS "placement" (
|
||||||
|
"id" UUID NOT NULL PRIMARY KEY,
|
||||||
|
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"deleted_at" TIMESTAMPTZ,
|
||||||
|
"wanted_placement_date" TIMESTAMPTZ NOT NULL,
|
||||||
|
"cost" DOUBLE PRECISION,
|
||||||
|
"comment" TEXT,
|
||||||
|
"status" VARCHAR(8) NOT NULL DEFAULT 'active',
|
||||||
|
"creative_id" UUID NOT NULL REFERENCES "creative" ("id") ON DELETE CASCADE,
|
||||||
|
"post_id" UUID REFERENCES "post" ("id") ON DELETE SET NULL,
|
||||||
|
"project_id" UUID NOT NULL REFERENCES "project" ("id") ON DELETE CASCADE,
|
||||||
|
"purchase_channel_id" UUID NOT NULL REFERENCES "purchase_channel" ("id") ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_placement_creativ_32111d" ON "placement" ("creative_id");
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_placement_post_id_e4a16e" ON "placement" ("post_id");
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_placement_project_2c6aa4" ON "placement" ("project_id");
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_placement_purchas_61f9e6" ON "placement" ("purchase_channel_id");
|
||||||
|
COMMENT ON COLUMN "placement"."status" IS 'ACTIVE: active\nARCHIVED: archived';
|
||||||
|
CREATE TABLE IF NOT EXISTS "subscription" (
|
||||||
|
"id" UUID NOT NULL PRIMARY KEY,
|
||||||
|
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"deleted_at" TIMESTAMPTZ,
|
||||||
|
"invite_link" VARCHAR(512) NOT NULL,
|
||||||
|
"status" VARCHAR(12) NOT NULL DEFAULT 'active',
|
||||||
|
"unsubscribed_at" TIMESTAMPTZ,
|
||||||
|
"placement_id" UUID NOT NULL REFERENCES "placement" ("id") ON DELETE CASCADE,
|
||||||
|
"telegram_user_id" UUID NOT NULL REFERENCES "telegram_user" ("id") ON DELETE CASCADE,
|
||||||
|
CONSTRAINT "uid_subscriptio_placeme_75b40b" UNIQUE ("placement_id", "telegram_user_id")
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_subscriptio_invite__f2058d" ON "subscription" ("invite_link");
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_subscriptio_placeme_5decb2" ON "subscription" ("placement_id");
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_subscriptio_telegra_8be9bb" ON "subscription" ("telegram_user_id");
|
||||||
|
COMMENT ON COLUMN "subscription"."status" IS 'ACTIVE: active\nUNSUBSCRIBED: unsubscribed';
|
||||||
|
CREATE TABLE IF NOT EXISTS "workspace_invite" (
|
||||||
|
"id" UUID NOT NULL PRIMARY KEY,
|
||||||
|
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"deleted_at" TIMESTAMPTZ,
|
||||||
|
"status" VARCHAR(8) NOT NULL DEFAULT 'pending',
|
||||||
|
"invited_by_id" UUID NOT NULL REFERENCES "user" ("id") ON DELETE CASCADE,
|
||||||
|
"user_id" UUID NOT NULL REFERENCES "user" ("id") ON DELETE CASCADE,
|
||||||
|
"workspace_id" UUID NOT NULL REFERENCES "workspace" ("id") ON DELETE CASCADE,
|
||||||
|
CONSTRAINT "uid_workspace_i_workspa_abd9c4" UNIQUE ("workspace_id", "user_id")
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_workspace_i_invited_3ed9f8" ON "workspace_invite" ("invited_by_id");
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_workspace_i_user_id_a96e1e" ON "workspace_invite" ("user_id");
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_workspace_i_workspa_71e9d2" ON "workspace_invite" ("workspace_id");
|
||||||
|
COMMENT ON COLUMN "workspace_invite"."status" IS 'PENDING: pending\nACCEPTED: accepted\nREVOKED: revoked';
|
||||||
|
CREATE TABLE IF NOT EXISTS "workspace_user" (
|
||||||
|
"id" UUID NOT NULL PRIMARY KEY,
|
||||||
|
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"deleted_at" TIMESTAMPTZ,
|
||||||
|
"status" VARCHAR(7) NOT NULL DEFAULT 'active',
|
||||||
|
"user_id" UUID NOT NULL REFERENCES "user" ("id") ON DELETE CASCADE,
|
||||||
|
"workspace_id" UUID NOT NULL REFERENCES "workspace" ("id") ON DELETE CASCADE,
|
||||||
|
CONSTRAINT "uid_workspace_u_workspa_13c46c" UNIQUE ("workspace_id", "user_id")
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_workspace_u_user_id_246318" ON "workspace_user" ("user_id");
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_workspace_u_workspa_a403b0" ON "workspace_user" ("workspace_id");
|
||||||
|
COMMENT ON COLUMN "workspace_user"."status" IS 'ACTIVE: active\nINVITED: invited\nBLOCKED: blocked';
|
||||||
|
CREATE TABLE IF NOT EXISTS "workspace_user_permission" (
|
||||||
|
"id" UUID NOT NULL PRIMARY KEY,
|
||||||
|
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"deleted_at" TIMESTAMPTZ,
|
||||||
|
"permission" VARCHAR(24) NOT NULL,
|
||||||
|
"workspace_user_id" UUID NOT NULL REFERENCES "workspace_user" ("id") ON DELETE CASCADE,
|
||||||
|
CONSTRAINT "uid_workspace_u_workspa_86f574" UNIQUE ("workspace_user_id", "permission")
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_workspace_u_workspa_b9b7b0" ON "workspace_user_permission" ("workspace_user_id");
|
||||||
|
COMMENT ON COLUMN "workspace_user_permission"."permission" IS 'ADMIN_FULL: admin_full\nPROJECTS_READ: projects_read\nPROJECTS_WRITE: projects_write\nCREATIVES_READ: creatives_read\nCREATIVES_WRITE: creatives_write\nPLACEMENTS_READ: placements_read\nPLACEMENTS_WRITE: placements_write\nANALYTICS_READ: analytics_read\nANALYTICS_WITHOUT_CLICKS: analytics_without_clicks\nCHANNELS_READ: channels_read\nCHANNELS_WRITE: channels_write';
|
||||||
|
CREATE TABLE IF NOT EXISTS "workspace_user_permission_scope" (
|
||||||
|
"id" UUID NOT NULL PRIMARY KEY,
|
||||||
|
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"deleted_at" TIMESTAMPTZ,
|
||||||
|
"permission" VARCHAR(24) NOT NULL,
|
||||||
|
"channel_id" UUID REFERENCES "channel" ("id") ON DELETE CASCADE,
|
||||||
|
"creative_id" UUID REFERENCES "creative" ("id") ON DELETE CASCADE,
|
||||||
|
"placement_id" UUID REFERENCES "placement" ("id") ON DELETE CASCADE,
|
||||||
|
"project_id" UUID REFERENCES "project" ("id") ON DELETE CASCADE,
|
||||||
|
"workspace_user_id" UUID NOT NULL REFERENCES "workspace_user" ("id") ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_workspace_u_channel_d94b48" ON "workspace_user_permission_scope" ("channel_id");
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_workspace_u_creativ_8da562" ON "workspace_user_permission_scope" ("creative_id");
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_workspace_u_placeme_ea1846" ON "workspace_user_permission_scope" ("placement_id");
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_workspace_u_project_bd7fea" ON "workspace_user_permission_scope" ("project_id");
|
||||||
|
CREATE INDEX IF NOT EXISTS "idx_workspace_u_workspa_82a35a" ON "workspace_user_permission_scope" ("workspace_user_id");
|
||||||
|
COMMENT ON COLUMN "workspace_user_permission_scope"."permission" IS 'ADMIN_FULL: admin_full\nPROJECTS_READ: projects_read\nPROJECTS_WRITE: projects_write\nCREATIVES_READ: creatives_read\nCREATIVES_WRITE: creatives_write\nPLACEMENTS_READ: placements_read\nPLACEMENTS_WRITE: placements_write\nANALYTICS_READ: analytics_read\nANALYTICS_WITHOUT_CLICKS: analytics_without_clicks\nCHANNELS_READ: channels_read\nCHANNELS_WRITE: channels_write';
|
||||||
|
CREATE TABLE IF NOT EXISTS "aerich" (
|
||||||
|
"id" SERIAL NOT NULL PRIMARY KEY,
|
||||||
|
"version" VARCHAR(255) NOT NULL,
|
||||||
|
"app" VARCHAR(100) NOT NULL,
|
||||||
|
"content" JSONB NOT NULL
|
||||||
|
);"""
|
||||||
|
|
||||||
|
|
||||||
|
async def downgrade(db: BaseDBAsyncClient) -> str:
|
||||||
|
return """
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
MODELS_STATE = (
|
||||||
|
'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'
|
||||||
|
'v0'
|
||||||
|
)
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
from tortoise import BaseDBAsyncClient
|
|
||||||
|
|
||||||
RUN_IN_TRANSACTION = True
|
|
||||||
|
|
||||||
|
|
||||||
async def upgrade(db: BaseDBAsyncClient) -> str:
|
|
||||||
return """
|
|
||||||
-- Add CHECK constraint: exactly one FK must be set
|
|
||||||
ALTER TABLE "workspace_user_permission_scope"
|
|
||||||
ADD CONSTRAINT "check_exactly_one_scope"
|
|
||||||
CHECK (
|
|
||||||
(("project_id" IS NOT NULL)::int +
|
|
||||||
("creative_id" IS NOT NULL)::int +
|
|
||||||
("placement_id" IS NOT NULL)::int +
|
|
||||||
("channel_id" IS NOT NULL)::int) = 1
|
|
||||||
);
|
|
||||||
|
|
||||||
COMMENT ON TABLE "workspace_user_permission_scope"
|
|
||||||
IS 'Permission scoped to specific entity (project/creative/placement/channel)'; \
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
async def downgrade(db: BaseDBAsyncClient) -> str:
|
|
||||||
return """
|
|
||||||
ALTER TABLE "workspace_user_permission_scope"
|
|
||||||
DROP CONSTRAINT IF EXISTS "check_exactly_one_scope"; \
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
MODELS_STATE = (
|
|
||||||
'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'
|
|
||||||
)
|
|
||||||
107
migrations/models/1_20260107150903_add_check_constraint.py
Normal file
107
migrations/models/1_20260107150903_add_check_constraint.py
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
from tortoise import BaseDBAsyncClient
|
||||||
|
|
||||||
|
RUN_IN_TRANSACTION = True
|
||||||
|
|
||||||
|
|
||||||
|
async def upgrade(db: BaseDBAsyncClient) -> str:
|
||||||
|
return """
|
||||||
|
-- Add CHECK constraint: exactly one FK must be set
|
||||||
|
ALTER TABLE "workspace_user_permission_scope"
|
||||||
|
ADD CONSTRAINT "check_exactly_one_scope"
|
||||||
|
CHECK (
|
||||||
|
(("project_id" IS NOT NULL)::int +
|
||||||
|
("creative_id" IS NOT NULL)::int +
|
||||||
|
("placement_id" IS NOT NULL)::int +
|
||||||
|
("channel_id" IS NOT NULL)::int) = 1
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON TABLE "workspace_user_permission_scope"
|
||||||
|
IS 'Permission scoped to specific entity (project/creative/placement/channel)'; \
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
async def downgrade(db: BaseDBAsyncClient) -> str:
|
||||||
|
return """
|
||||||
|
ALTER TABLE "workspace_user_permission_scope"
|
||||||
|
DROP CONSTRAINT IF EXISTS "check_exactly_one_scope"; \
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
MODELS_STATE = (
|
||||||
|
'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'
|
||||||
|
'v0'
|
||||||
|
)
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
from tortoise import BaseDBAsyncClient
|
|
||||||
|
|
||||||
RUN_IN_TRANSACTION = True
|
|
||||||
|
|
||||||
|
|
||||||
async def upgrade(db: BaseDBAsyncClient) -> str:
|
|
||||||
return """
|
|
||||||
ALTER TABLE "creative" ADD "buttons" JSONB NOT NULL;
|
|
||||||
ALTER TABLE "creative" ADD "media_data" BYTEA;
|
|
||||||
ALTER TABLE "creative" ADD "media_file_id" VARCHAR(512);
|
|
||||||
ALTER TABLE "creative" ADD "media_type" VARCHAR(32);"""
|
|
||||||
|
|
||||||
|
|
||||||
async def downgrade(db: BaseDBAsyncClient) -> str:
|
|
||||||
return """
|
|
||||||
ALTER TABLE "creative" DROP COLUMN "buttons";
|
|
||||||
ALTER TABLE "creative" DROP COLUMN "media_data";
|
|
||||||
ALTER TABLE "creative" DROP COLUMN "media_file_id";
|
|
||||||
ALTER TABLE "creative" DROP COLUMN "media_type";"""
|
|
||||||
|
|
||||||
|
|
||||||
MODELS_STATE = (
|
|
||||||
"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"
|
|
||||||
"a4"
|
|
||||||
)
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
from tortoise import BaseDBAsyncClient
|
|
||||||
|
|
||||||
RUN_IN_TRANSACTION = True
|
|
||||||
|
|
||||||
|
|
||||||
async def upgrade(db: BaseDBAsyncClient) -> str:
|
|
||||||
return """
|
|
||||||
ALTER TABLE "creative" ADD "media_s3_key" VARCHAR(512);
|
|
||||||
ALTER TABLE "creative" DROP COLUMN "media_data";"""
|
|
||||||
|
|
||||||
|
|
||||||
async def downgrade(db: BaseDBAsyncClient) -> str:
|
|
||||||
return """
|
|
||||||
ALTER TABLE "creative" ADD "media_data" BYTEA;
|
|
||||||
ALTER TABLE "creative" DROP COLUMN "media_s3_key";"""
|
|
||||||
|
|
||||||
|
|
||||||
MODELS_STATE = (
|
|
||||||
"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"
|
|
||||||
"XdLO/D8vv/8PAyERnQ=="
|
|
||||||
)
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
from tortoise import BaseDBAsyncClient
|
|
||||||
|
|
||||||
RUN_IN_TRANSACTION = True
|
|
||||||
|
|
||||||
|
|
||||||
async def upgrade(db: BaseDBAsyncClient) -> str:
|
|
||||||
return """
|
|
||||||
ALTER TABLE "purchase"
|
|
||||||
ADD "comment" TEXT;
|
|
||||||
ALTER TABLE "purchase"
|
|
||||||
ADD "payment_at" TIMESTAMPTZ;
|
|
||||||
ALTER TABLE "purchase"
|
|
||||||
ADD "format" TEXT;
|
|
||||||
ALTER TABLE "purchase"
|
|
||||||
ADD "cost_before_bargain" DOUBLE PRECISION;
|
|
||||||
ALTER TABLE "purchase"
|
|
||||||
ADD "placement_at" TIMESTAMPTZ;
|
|
||||||
ALTER TABLE "purchase"
|
|
||||||
ADD "cost_type" VARCHAR(8);
|
|
||||||
ALTER TABLE "purchase"
|
|
||||||
ADD "cost_value" DOUBLE PRECISION;
|
|
||||||
ALTER TABLE "purchase"
|
|
||||||
ADD "purchase_type" VARCHAR(16);
|
|
||||||
ALTER TABLE "purchase_channel"
|
|
||||||
ADD "format" TEXT;
|
|
||||||
ALTER TABLE "purchase_channel"
|
|
||||||
ADD "cost_before_bargain" DOUBLE PRECISION;
|
|
||||||
ALTER TABLE "purchase_channel"
|
|
||||||
ADD "placement_at" TIMESTAMPTZ;
|
|
||||||
ALTER TABLE "purchase_channel"
|
|
||||||
ADD "cost_type" VARCHAR(8);
|
|
||||||
ALTER TABLE "purchase_channel"
|
|
||||||
ADD "cost_value" DOUBLE PRECISION;
|
|
||||||
ALTER TABLE "purchase_channel"
|
|
||||||
DROP COLUMN "planned_cost";
|
|
||||||
COMMENT ON COLUMN "purchase"."cost_type" IS 'FIXED: fixed\nCPM: cpm';
|
|
||||||
COMMENT ON COLUMN "purchase"."purchase_type" IS 'SELF_PROMO: self_promo\nSTANDARD: standard';
|
|
||||||
COMMENT ON COLUMN "purchase_channel"."cost_type" IS 'FIXED: fixed\nCPM: cpm';"""
|
|
||||||
|
|
||||||
|
|
||||||
async def downgrade(db: BaseDBAsyncClient) -> str:
|
|
||||||
return """
|
|
||||||
ALTER TABLE "purchase"
|
|
||||||
DROP COLUMN "comment";
|
|
||||||
ALTER TABLE "purchase"
|
|
||||||
DROP COLUMN "payment_at";
|
|
||||||
ALTER TABLE "purchase"
|
|
||||||
DROP COLUMN "format";
|
|
||||||
ALTER TABLE "purchase"
|
|
||||||
DROP COLUMN "cost_before_bargain";
|
|
||||||
ALTER TABLE "purchase"
|
|
||||||
DROP COLUMN "placement_at";
|
|
||||||
ALTER TABLE "purchase"
|
|
||||||
DROP COLUMN "cost_type";
|
|
||||||
ALTER TABLE "purchase"
|
|
||||||
DROP COLUMN "cost_value";
|
|
||||||
ALTER TABLE "purchase"
|
|
||||||
DROP COLUMN "purchase_type";
|
|
||||||
ALTER TABLE "purchase_channel"
|
|
||||||
ADD "planned_cost" DOUBLE PRECISION;
|
|
||||||
ALTER TABLE "purchase_channel"
|
|
||||||
DROP COLUMN "format";
|
|
||||||
ALTER TABLE "purchase_channel"
|
|
||||||
DROP COLUMN "cost_before_bargain";
|
|
||||||
ALTER TABLE "purchase_channel"
|
|
||||||
DROP COLUMN "placement_at";
|
|
||||||
ALTER TABLE "purchase_channel"
|
|
||||||
DROP COLUMN "cost_type";
|
|
||||||
ALTER TABLE "purchase_channel"
|
|
||||||
DROP COLUMN "cost_value";"""
|
|
||||||
|
|
||||||
|
|
||||||
MODELS_STATE = (
|
|
||||||
'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'
|
|
||||||
'49ruDVq8qUI6aKVL2pn/5+X3/wGuY8FA'
|
|
||||||
)
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
from tortoise import BaseDBAsyncClient
|
|
||||||
|
|
||||||
RUN_IN_TRANSACTION = True
|
|
||||||
|
|
||||||
|
|
||||||
async def upgrade(db: BaseDBAsyncClient) -> str:
|
|
||||||
return """
|
|
||||||
DROP INDEX IF EXISTS "uid_user_telegra_66ffbd";
|
|
||||||
ALTER TABLE "subscription" DROP CONSTRAINT IF EXISTS "uid_subscriptio_placeme_8fcb8f";
|
|
||||||
ALTER TABLE "subscription" DROP CONSTRAINT IF EXISTS "fk_subscrip_user_6b9f4b8e";
|
|
||||||
CREATE TABLE IF NOT EXISTS "telegram_user" (
|
|
||||||
"id" UUID NOT NULL PRIMARY KEY,
|
|
||||||
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"deleted_at" TIMESTAMPTZ,
|
|
||||||
"telegram_id" BIGINT NOT NULL UNIQUE,
|
|
||||||
"username" VARCHAR(255),
|
|
||||||
"first_name" VARCHAR(255),
|
|
||||||
"last_name" VARCHAR(255)
|
|
||||||
);
|
|
||||||
CREATE INDEX IF NOT EXISTS "idx_telegram_us_telegra_3a3100" ON "telegram_user" ("telegram_id");
|
|
||||||
INSERT INTO "telegram_user" (
|
|
||||||
"id",
|
|
||||||
"created_at",
|
|
||||||
"updated_at",
|
|
||||||
"deleted_at",
|
|
||||||
"telegram_id",
|
|
||||||
"username",
|
|
||||||
"first_name",
|
|
||||||
"last_name"
|
|
||||||
)
|
|
||||||
SELECT
|
|
||||||
"id",
|
|
||||||
"created_at",
|
|
||||||
"updated_at",
|
|
||||||
"deleted_at",
|
|
||||||
"telegram_id",
|
|
||||||
"username",
|
|
||||||
"first_name",
|
|
||||||
"last_name"
|
|
||||||
FROM "user"
|
|
||||||
WHERE "telegram_id" IS NOT NULL
|
|
||||||
ON CONFLICT ("id") DO NOTHING;
|
|
||||||
ALTER TABLE "subscription" RENAME COLUMN "subscriber_id" TO "telegram_user_id";
|
|
||||||
ALTER TABLE "user" ADD "telegram_user_id" UUID UNIQUE;
|
|
||||||
UPDATE "user" SET "telegram_user_id" = "id";
|
|
||||||
ALTER TABLE "user" ALTER COLUMN "telegram_user_id" SET NOT NULL;
|
|
||||||
ALTER TABLE "user" DROP COLUMN "first_name";
|
|
||||||
ALTER TABLE "user" DROP COLUMN "username";
|
|
||||||
ALTER TABLE "user" DROP COLUMN "telegram_id";
|
|
||||||
ALTER TABLE "user" DROP COLUMN "last_name";
|
|
||||||
ALTER TABLE "subscription" ADD CONSTRAINT "fk_subscrip_telegram_ab72b5bf" FOREIGN KEY ("telegram_user_id") REFERENCES "telegram_user" ("id") ON DELETE CASCADE;
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS "uid_subscriptio_placeme_75b40b" ON "subscription" ("placement_id", "telegram_user_id");
|
|
||||||
ALTER TABLE "user" ADD CONSTRAINT "fk_user_telegram_548d951e" FOREIGN KEY ("telegram_user_id") REFERENCES "telegram_user" ("id") ON DELETE CASCADE;"""
|
|
||||||
|
|
||||||
|
|
||||||
async def downgrade(db: BaseDBAsyncClient) -> str:
|
|
||||||
return """
|
|
||||||
DROP INDEX IF EXISTS "uid_subscriptio_placeme_75b40b";
|
|
||||||
ALTER TABLE "subscription" DROP CONSTRAINT IF EXISTS "fk_subscrip_telegram_ab72b5bf";
|
|
||||||
ALTER TABLE "user" DROP CONSTRAINT IF EXISTS "fk_user_telegram_548d951e";
|
|
||||||
ALTER TABLE "user" ADD "first_name" VARCHAR(255);
|
|
||||||
ALTER TABLE "user" ADD "username" VARCHAR(255);
|
|
||||||
ALTER TABLE "user" ADD "telegram_id" BIGINT NOT NULL UNIQUE;
|
|
||||||
ALTER TABLE "user" ADD "last_name" VARCHAR(255);
|
|
||||||
ALTER TABLE "user" DROP COLUMN "telegram_user_id";
|
|
||||||
ALTER TABLE "subscription" RENAME COLUMN "telegram_user_id" TO "subscriber_id";
|
|
||||||
DROP TABLE IF EXISTS "telegram_user";
|
|
||||||
ALTER TABLE "subscription" ADD CONSTRAINT "fk_subscrip_user_6b9f4b8e" FOREIGN KEY ("subscriber_id") REFERENCES "user" ("id") ON DELETE CASCADE;
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS "uid_subscriptio_placeme_8fcb8f" ON "subscription" ("placement_id", "subscriber_id");"""
|
|
||||||
|
|
||||||
|
|
||||||
MODELS_STATE = (
|
|
||||||
"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"
|
|
||||||
"1lQhHbSyJe3k/3n5/X8nR5KJ"
|
|
||||||
)
|
|
||||||
@@ -291,11 +291,11 @@ class Postgres(DatabaseBase, Database):
|
|||||||
)
|
)
|
||||||
|
|
||||||
async def get_purchase(self, workspace_id: uuid.UUID, purchase_id: uuid.UUID) -> domain.Purchase | None:
|
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, workspace_id=workspace_id)
|
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]:
|
async def get_project_purchases(self, workspace_id: uuid.UUID, project_id: uuid.UUID) -> list[domain.Purchase]:
|
||||||
return (
|
return (
|
||||||
await domain.Purchase.filter(workspace_id=workspace_id, project_id=project_id)
|
await domain.Purchase.filter(project__workspace_id=workspace_id, project_id=project_id)
|
||||||
.prefetch_related('channels', 'channels__channel')
|
.prefetch_related('channels', 'channels__channel')
|
||||||
.all()
|
.all()
|
||||||
)
|
)
|
||||||
@@ -315,6 +315,13 @@ class Postgres(DatabaseBase, Database):
|
|||||||
.first()
|
.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]:
|
async def get_purchase_channels_by_project(self, project_id: uuid.UUID) -> list[domain.PurchaseChannel]:
|
||||||
return (
|
return (
|
||||||
await domain.PurchaseChannel.filter(
|
await domain.PurchaseChannel.filter(
|
||||||
@@ -378,9 +385,9 @@ class Postgres(DatabaseBase, Database):
|
|||||||
await domain.PurchaseChannel.filter(purchase_id=purchase_id, channel_id=channel_id).delete()
|
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:
|
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, workspace_id=workspace_id).prefetch_related(
|
return await domain.Creative.get_or_none(
|
||||||
'project', 'project__channel'
|
id=creative_id, project__workspace_id=workspace_id
|
||||||
)
|
).prefetch_related('project', 'project__channel')
|
||||||
|
|
||||||
async def update_creative(self, creative: domain.Creative) -> None:
|
async def update_creative(self, creative: domain.Creative) -> None:
|
||||||
await creative.save()
|
await creative.save()
|
||||||
@@ -392,7 +399,7 @@ class Postgres(DatabaseBase, Database):
|
|||||||
include_archived: bool = False,
|
include_archived: bool = False,
|
||||||
allowed_project_ids: set[uuid.UUID] | None = None,
|
allowed_project_ids: set[uuid.UUID] | None = None,
|
||||||
) -> list[domain.Creative]:
|
) -> list[domain.Creative]:
|
||||||
query = domain.Creative.filter(workspace_id=workspace_id)
|
query = domain.Creative.filter(project__workspace_id=workspace_id)
|
||||||
|
|
||||||
if project_id:
|
if project_id:
|
||||||
query = query.filter(project_id=project_id)
|
query = query.filter(project_id=project_id)
|
||||||
@@ -410,8 +417,16 @@ class Postgres(DatabaseBase, Database):
|
|||||||
await domain.Creative.filter(id=creative_id).delete()
|
await domain.Creative.filter(id=creative_id).delete()
|
||||||
|
|
||||||
async def get_placement(self, workspace_id: uuid.UUID, placement_id: uuid.UUID) -> domain.Placement | None:
|
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, workspace_id=workspace_id).prefetch_related(
|
return await domain.Placement.get_or_none(
|
||||||
'project', 'project__channel', 'placement_channel', 'creative', 'post', 'post__channel'
|
id=placement_id, project__workspace_id=workspace_id
|
||||||
|
).prefetch_related(
|
||||||
|
'project',
|
||||||
|
'project__channel',
|
||||||
|
'purchase_channel',
|
||||||
|
'purchase_channel__channel',
|
||||||
|
'creative',
|
||||||
|
'post',
|
||||||
|
'post__channel',
|
||||||
)
|
)
|
||||||
|
|
||||||
async def create_placement(self, placement: domain.Placement) -> None:
|
async def create_placement(self, placement: domain.Placement) -> None:
|
||||||
@@ -431,7 +446,7 @@ class Postgres(DatabaseBase, Database):
|
|||||||
date_from: datetime.datetime | None = None,
|
date_from: datetime.datetime | None = None,
|
||||||
date_to: datetime.datetime | None = None,
|
date_to: datetime.datetime | None = None,
|
||||||
) -> list[domain.Placement]:
|
) -> list[domain.Placement]:
|
||||||
query = domain.Placement.filter(workspace_id=workspace_id)
|
query = domain.Placement.filter(project__workspace_id=workspace_id)
|
||||||
|
|
||||||
if project_id:
|
if project_id:
|
||||||
query = query.filter(project_id=project_id)
|
query = query.filter(project_id=project_id)
|
||||||
@@ -440,7 +455,7 @@ class Postgres(DatabaseBase, Database):
|
|||||||
return []
|
return []
|
||||||
query = query.filter(project_id__in=list(allowed_project_ids))
|
query = query.filter(project_id__in=list(allowed_project_ids))
|
||||||
if placement_channel_id:
|
if placement_channel_id:
|
||||||
query = query.filter(placement_channel_id=placement_channel_id)
|
query = query.filter(purchase_channel__channel_id=placement_channel_id)
|
||||||
if creative_id:
|
if creative_id:
|
||||||
query = query.filter(creative_id=creative_id)
|
query = query.filter(creative_id=creative_id)
|
||||||
|
|
||||||
@@ -454,7 +469,7 @@ class Postgres(DatabaseBase, Database):
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
await query.prefetch_related(
|
await query.prefetch_related(
|
||||||
'project', 'project__channel', 'placement_channel', 'creative', 'post', 'post__channel'
|
'project', 'project__channel', 'purchase_channel', 'purchase_channel__channel', 'creative', 'post', 'post__channel'
|
||||||
)
|
)
|
||||||
.order_by('-wanted_placement_date')
|
.order_by('-wanted_placement_date')
|
||||||
.all()
|
.all()
|
||||||
@@ -464,9 +479,9 @@ class Postgres(DatabaseBase, Database):
|
|||||||
await domain.Placement.filter(id=placement_id).delete()
|
await domain.Placement.filter(id=placement_id).delete()
|
||||||
|
|
||||||
async def get_placement_by_invite_link(self, invite_link: str) -> domain.Placement | None:
|
async def get_placement_by_invite_link(self, invite_link: str) -> domain.Placement | None:
|
||||||
return await domain.Placement.get_or_none(invite_link=invite_link).prefetch_related(
|
return await domain.Placement.get_or_none(
|
||||||
'project', 'project__channel', 'placement_channel', 'creative'
|
purchase_channel__invite_link=invite_link
|
||||||
)
|
).prefetch_related('project', 'project__channel', 'purchase_channel', 'purchase_channel__channel', 'creative')
|
||||||
|
|
||||||
async def create_subscription(self, subscription: domain.Subscription) -> None:
|
async def create_subscription(self, subscription: domain.Subscription) -> None:
|
||||||
await subscription.save()
|
await subscription.save()
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ __all__ = (
|
|||||||
'WorkspaceAccessDenied',
|
'WorkspaceAccessDenied',
|
||||||
'WorkspaceInviteNotFound',
|
'WorkspaceInviteNotFound',
|
||||||
'WorkspaceInviteAlreadyExists',
|
'WorkspaceInviteAlreadyExists',
|
||||||
|
'WorkspaceInviteAlreadyProcessed',
|
||||||
'WorkspaceMemberAlreadyExists',
|
'WorkspaceMemberAlreadyExists',
|
||||||
'LoginTokenNotFound',
|
'LoginTokenNotFound',
|
||||||
'LoginTokenExpired',
|
'LoginTokenExpired',
|
||||||
@@ -84,6 +85,7 @@ from .error import (
|
|||||||
UserNotFound,
|
UserNotFound,
|
||||||
WorkspaceAccessDenied,
|
WorkspaceAccessDenied,
|
||||||
WorkspaceInviteAlreadyExists,
|
WorkspaceInviteAlreadyExists,
|
||||||
|
WorkspaceInviteAlreadyProcessed,
|
||||||
WorkspaceInviteNotFound,
|
WorkspaceInviteNotFound,
|
||||||
WorkspaceMemberAlreadyExists,
|
WorkspaceMemberAlreadyExists,
|
||||||
WorkspaceNotFound,
|
WorkspaceNotFound,
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ from .error import CreativeInviteLinkNotFound, CreativeMediaTooLarge, CreativeMu
|
|||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .project import Project
|
from .project import Project
|
||||||
from .workspace import Workspace
|
|
||||||
|
|
||||||
|
|
||||||
class CreativeStatus(str, enum.Enum):
|
class CreativeStatus(str, enum.Enum):
|
||||||
@@ -27,15 +26,11 @@ class Creative(TimestampedModel):
|
|||||||
buttons = fields.JSONField(default=list)
|
buttons = fields.JSONField(default=list)
|
||||||
status = fields.CharEnumField(CreativeStatus, default=CreativeStatus.ACTIVE)
|
status = fields.CharEnumField(CreativeStatus, default=CreativeStatus.ACTIVE)
|
||||||
|
|
||||||
workspace: fields.ForeignKeyRelation['Workspace'] = fields.ForeignKeyField(
|
|
||||||
'models.Workspace', related_name='creatives', on_delete=fields.CASCADE, index=True
|
|
||||||
)
|
|
||||||
project: fields.ForeignKeyRelation['Project'] = fields.ForeignKeyField(
|
project: fields.ForeignKeyRelation['Project'] = fields.ForeignKeyField(
|
||||||
'models.Project', related_name='creatives', on_delete=fields.CASCADE, index=True
|
'models.Project', related_name='creatives', on_delete=fields.CASCADE, index=True
|
||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
workspace_id: UUID
|
|
||||||
project_id: UUID
|
project_id: UUID
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|||||||
@@ -7,12 +7,10 @@ from tortoise import fields
|
|||||||
from .base import TimestampedModel
|
from .base import TimestampedModel
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .channel import Channel
|
|
||||||
from .creative import Creative
|
from .creative import Creative
|
||||||
from .post import Post
|
from .post import Post
|
||||||
from .project import Project
|
from .project import Project
|
||||||
from .purchase import Purchase, PurchaseChannel
|
from .purchase import PurchaseChannel
|
||||||
from .workspace import Workspace
|
|
||||||
|
|
||||||
|
|
||||||
class PlacementStatus(str, enum.Enum):
|
class PlacementStatus(str, enum.Enum):
|
||||||
@@ -31,27 +29,17 @@ class Placement(TimestampedModel):
|
|||||||
wanted_placement_date = fields.DatetimeField(db_column='placement_date')
|
wanted_placement_date = fields.DatetimeField(db_column='placement_date')
|
||||||
cost = fields.FloatField(null=True)
|
cost = fields.FloatField(null=True)
|
||||||
comment = fields.TextField(null=True)
|
comment = fields.TextField(null=True)
|
||||||
invite_link = fields.CharField(max_length=512)
|
|
||||||
|
|
||||||
status = fields.CharEnumField(PlacementStatus, default=PlacementStatus.ACTIVE)
|
status = fields.CharEnumField(PlacementStatus, default=PlacementStatus.ACTIVE)
|
||||||
|
|
||||||
workspace: fields.ForeignKeyRelation['Workspace'] = fields.ForeignKeyField(
|
|
||||||
'models.Workspace', related_name='placements', on_delete=fields.CASCADE, index=True
|
|
||||||
)
|
|
||||||
project: fields.ForeignKeyRelation['Project'] = fields.ForeignKeyField(
|
project: fields.ForeignKeyRelation['Project'] = fields.ForeignKeyField(
|
||||||
'models.Project', related_name='placements', on_delete=fields.CASCADE, index=True
|
'models.Project', related_name='placements', on_delete=fields.CASCADE, index=True
|
||||||
)
|
)
|
||||||
creative: fields.ForeignKeyRelation['Creative'] = fields.ForeignKeyField(
|
creative: fields.ForeignKeyRelation['Creative'] = fields.ForeignKeyField(
|
||||||
'models.Creative', related_name='placements', on_delete=fields.CASCADE, index=True
|
'models.Creative', related_name='placements', on_delete=fields.CASCADE, index=True
|
||||||
)
|
)
|
||||||
placement_channel: fields.ForeignKeyRelation['Channel'] = fields.ForeignKeyField(
|
purchase_channel: fields.ForeignKeyRelation['PurchaseChannel'] = fields.ForeignKeyField(
|
||||||
'models.Channel', related_name='placements', on_delete=fields.CASCADE, index=True
|
'models.PurchaseChannel', related_name='placements', on_delete=fields.CASCADE, index=True
|
||||||
)
|
|
||||||
purchase: fields.ForeignKeyRelation['Purchase'] | None = fields.ForeignKeyField(
|
|
||||||
'models.Purchase', related_name='placements', on_delete=fields.CASCADE, null=True, index=True
|
|
||||||
)
|
|
||||||
purchase_channel: fields.ForeignKeyRelation['PurchaseChannel'] | None = fields.ForeignKeyField(
|
|
||||||
'models.PurchaseChannel', related_name='placements', on_delete=fields.CASCADE, null=True, index=True
|
|
||||||
)
|
)
|
||||||
|
|
||||||
post: fields.ForeignKeyRelation['Post'] | None = fields.ForeignKeyField(
|
post: fields.ForeignKeyRelation['Post'] | None = fields.ForeignKeyField(
|
||||||
@@ -59,13 +47,10 @@ class Placement(TimestampedModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
workspace_id: UUID
|
|
||||||
project_id: UUID
|
project_id: UUID
|
||||||
creative_id: UUID
|
creative_id: UUID
|
||||||
placement_channel_id: UUID
|
|
||||||
purchase_id: UUID | None
|
|
||||||
post_id: UUID | None
|
post_id: UUID | None
|
||||||
purchase_channel_id: UUID | None
|
purchase_channel_id: UUID
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
table = 'placement'
|
table = 'placement'
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ if TYPE_CHECKING:
|
|||||||
from .channel import Channel
|
from .channel import Channel
|
||||||
from .creative import Creative
|
from .creative import Creative
|
||||||
from .project import Project
|
from .project import Project
|
||||||
from .workspace import Workspace
|
|
||||||
|
|
||||||
|
|
||||||
class PurchaseStatus(enum.StrEnum):
|
class PurchaseStatus(enum.StrEnum):
|
||||||
@@ -54,9 +53,9 @@ class Purchase(TimestampedModel):
|
|||||||
'models.Creative', related_name='purchases', on_delete=fields.CASCADE, index=True
|
'models.Creative', related_name='purchases', on_delete=fields.CASCADE, index=True
|
||||||
)
|
)
|
||||||
|
|
||||||
workspace: fields.ForeignKeyRelation['Workspace'] = fields.ForeignKeyField(
|
if TYPE_CHECKING:
|
||||||
'models.Workspace', related_name='purchases', on_delete=fields.CASCADE, index=True
|
project_id: uuid.UUID
|
||||||
)
|
creative_id: uuid.UUID
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
table = 'purchase'
|
table = 'purchase'
|
||||||
|
|||||||
@@ -47,9 +47,7 @@ class GetPlacementInput(pydantic.BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class CreatePlacementInput(pydantic.BaseModel):
|
class CreatePlacementInput(pydantic.BaseModel):
|
||||||
project_id: uuid.UUID
|
purchase_channel_id: uuid.UUID
|
||||||
placement_channel_id: uuid.UUID
|
|
||||||
creative_id: uuid.UUID
|
|
||||||
placement_date: datetime.datetime
|
placement_date: datetime.datetime
|
||||||
cost: float | None = None
|
cost: float | None = None
|
||||||
comment: str | None = None
|
comment: str | None = None
|
||||||
|
|||||||
@@ -175,6 +175,8 @@ class Database(typing.Protocol):
|
|||||||
self, purchase_channel_id: UUID, purchase_id: UUID
|
self, purchase_channel_id: UUID, purchase_id: UUID
|
||||||
) -> domain.PurchaseChannel | None: ...
|
) -> domain.PurchaseChannel | None: ...
|
||||||
|
|
||||||
|
async def get_purchase_channel_by_id(self, purchase_channel_id: UUID) -> domain.PurchaseChannel | None: ...
|
||||||
|
|
||||||
async def get_purchase_channels_by_project(self, project_id: UUID) -> list[domain.PurchaseChannel]: ...
|
async def get_purchase_channels_by_project(self, project_id: UUID) -> list[domain.PurchaseChannel]: ...
|
||||||
|
|
||||||
async def add_channel_to_purchase(
|
async def add_channel_to_purchase(
|
||||||
@@ -225,6 +227,10 @@ class Database(typing.Protocol):
|
|||||||
date_to: datetime.datetime | None = None,
|
date_to: datetime.datetime | None = None,
|
||||||
) -> list[domain.Placement]: ...
|
) -> list[domain.Placement]: ...
|
||||||
|
|
||||||
|
async def create_placement(self, placement: domain.Placement) -> None: ...
|
||||||
|
|
||||||
|
async def update_placement(self, placement: domain.Placement) -> None: ...
|
||||||
|
|
||||||
async def delete_placement(self, placement_id: UUID) -> None: ...
|
async def delete_placement(self, placement_id: UUID) -> None: ...
|
||||||
|
|
||||||
async def get_placement_by_invite_link(self, invite_link: str) -> domain.Placement | None: ...
|
async def get_placement_by_invite_link(self, invite_link: str) -> domain.Placement | None: ...
|
||||||
@@ -398,7 +404,7 @@ class Usecase:
|
|||||||
if purchase:
|
if purchase:
|
||||||
return purchase
|
return purchase
|
||||||
|
|
||||||
purchase = domain.Purchase(workspace_id=project.workspace_id, project_id=project.id, creative_id=creative.id)
|
purchase = domain.Purchase(project_id=project.id, creative_id=creative.id)
|
||||||
await self.database.create_purchase(purchase)
|
await self.database.create_purchase(purchase)
|
||||||
return purchase
|
return purchase
|
||||||
|
|
||||||
|
|||||||
@@ -47,8 +47,9 @@ async def get_channel_analytics(
|
|||||||
if input.project_id is None and allowed_project_ids is not None:
|
if input.project_id is None and allowed_project_ids is not None:
|
||||||
channel_map: dict[UUID, domain.Channel] = {}
|
channel_map: dict[UUID, domain.Channel] = {}
|
||||||
for placement in placements:
|
for placement in placements:
|
||||||
if placement.placement_channel:
|
purchase_channel = placement.purchase_channel
|
||||||
channel_map[placement.placement_channel_id] = placement.placement_channel
|
if purchase_channel and purchase_channel.channel:
|
||||||
|
channel_map[purchase_channel.channel_id] = purchase_channel.channel
|
||||||
channels = list(channel_map.values())
|
channels = list(channel_map.values())
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -69,7 +70,10 @@ async def get_channel_analytics(
|
|||||||
subscriptions_counts = await self.database.count_subscriptions_by_placement_batch(placement_ids)
|
subscriptions_counts = await self.database.count_subscriptions_by_placement_batch(placement_ids)
|
||||||
|
|
||||||
for p in placements:
|
for p in placements:
|
||||||
stats = channel_stats.get(p.placement_channel_id)
|
purchase_channel = p.purchase_channel
|
||||||
|
if not purchase_channel:
|
||||||
|
continue
|
||||||
|
stats = channel_stats.get(purchase_channel.channel_id)
|
||||||
if stats is None:
|
if stats is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|||||||
@@ -131,19 +131,21 @@ async def get_overview_analytics(
|
|||||||
project_meta: dict[UUID, domain.Project] = {}
|
project_meta: dict[UUID, domain.Project] = {}
|
||||||
|
|
||||||
for placement in current_placements:
|
for placement in current_placements:
|
||||||
placement_date = placement.wanted_placement_date.date()
|
placement_day = placement.wanted_placement_date.date()
|
||||||
project_meta[placement.project_id] = placement.project
|
project_meta[placement.project_id] = placement.project
|
||||||
|
|
||||||
cost_value = placement.cost or 0.0
|
cost_value = placement.cost or 0.0
|
||||||
cost_per_day[placement_date] += cost_value
|
cost_per_day[placement_day] += cost_value
|
||||||
project_spending_map[placement.project_id] += cost_value
|
project_spending_map[placement.project_id] += cost_value
|
||||||
subs = subs_per_placement_current.get(placement.id, 0)
|
subs = subs_per_placement_current.get(placement.id, 0)
|
||||||
|
|
||||||
channel_id = placement.placement_channel_id
|
purchase_channel = placement.purchase_channel
|
||||||
if channel_id not in channel_stats:
|
if purchase_channel:
|
||||||
channel_stats[channel_id] = ChannelAggregate(channel=placement.placement_channel)
|
channel_id = purchase_channel.channel_id
|
||||||
channel_stats[channel_id].total_cost += cost_value
|
if channel_id not in channel_stats:
|
||||||
channel_stats[channel_id].total_subs += subs
|
channel_stats[channel_id] = ChannelAggregate(channel=purchase_channel.channel)
|
||||||
|
channel_stats[channel_id].total_cost += cost_value
|
||||||
|
channel_stats[channel_id].total_subs += subs
|
||||||
|
|
||||||
start_date = input.date_from.date()
|
start_date = input.date_from.date()
|
||||||
end_date = input.date_to.date()
|
end_date = input.date_to.date()
|
||||||
|
|||||||
@@ -57,8 +57,8 @@ async def get_placements_analytics(
|
|||||||
id=p.id,
|
id=p.id,
|
||||||
project_id=p.project_id,
|
project_id=p.project_id,
|
||||||
project_channel_title=p.project.channel.title,
|
project_channel_title=p.project.channel.title,
|
||||||
placement_channel_id=p.placement_channel_id,
|
placement_channel_id=p.purchase_channel.channel_id,
|
||||||
placement_channel_title=p.placement_channel.title,
|
placement_channel_title=p.purchase_channel.channel.title,
|
||||||
creative_id=p.creative_id,
|
creative_id=p.creative_id,
|
||||||
creative_name=p.creative.name,
|
creative_name=p.creative.name,
|
||||||
placement_date=p.wanted_placement_date,
|
placement_date=p.wanted_placement_date,
|
||||||
|
|||||||
@@ -47,4 +47,5 @@ async def attach_channel_to_workspace(
|
|||||||
title=channel.title,
|
title=channel.title,
|
||||||
username=channel.username,
|
username=channel.username,
|
||||||
status=project.status,
|
status=project.status,
|
||||||
|
purchase_invite_type_default=project.purchase_invite_type_default,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ async def create_creative(
|
|||||||
buttons=[button.model_dump() for button in input.buttons],
|
buttons=[button.model_dump() for button in input.buttons],
|
||||||
status=domain.CreativeStatus.ACTIVE,
|
status=domain.CreativeStatus.ACTIVE,
|
||||||
project_id=project.id,
|
project_id=project.id,
|
||||||
workspace_id=workspace_id,
|
|
||||||
)
|
)
|
||||||
await self.database.create_creative(creative)
|
await self.database.create_creative(creative)
|
||||||
|
|
||||||
|
|||||||
@@ -1,84 +0,0 @@
|
|||||||
import logging
|
|
||||||
import uuid
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
import domain.purchase
|
|
||||||
from src import domain, dto
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from .. import Usecase
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
async def create_placement(
|
|
||||||
self: 'Usecase', input: dto.CreatePlacementInput, user_id: uuid.UUID, workspace_id: uuid.UUID
|
|
||||||
) -> dto.PlacementOutput:
|
|
||||||
await self.ensure_workspace_permission(
|
|
||||||
workspace_id,
|
|
||||||
user_id,
|
|
||||||
domain.PermissionKey.PLACEMENTS_WRITE,
|
|
||||||
for_project_id=input.project_id,
|
|
||||||
)
|
|
||||||
|
|
||||||
project = await self.database.get_project(workspace_id, project_id=input.project_id)
|
|
||||||
if not project:
|
|
||||||
raise domain.ProjectNotFound(input.project_id)
|
|
||||||
|
|
||||||
placement_channel = await self.database.get_channel(channel_id=input.placement_channel_id)
|
|
||||||
if not placement_channel:
|
|
||||||
raise domain.ChannelNotFound(input.placement_channel_id)
|
|
||||||
|
|
||||||
creative = await self.database.get_creative(workspace_id, input.creative_id)
|
|
||||||
if not creative:
|
|
||||||
raise domain.CreativeNotFound(input.creative_id)
|
|
||||||
if creative.project_id != project.id:
|
|
||||||
raise domain.CreativeNotFound(input.creative_id)
|
|
||||||
|
|
||||||
# Берем тип ссылки из настроек проекта
|
|
||||||
requires_approval = project.purchase_invite_type_default == domain.purchase.InviteLinkType.APPROVAL
|
|
||||||
|
|
||||||
if project.channel.telegram_id is None:
|
|
||||||
raise domain.ChannelNotFound(project.channel.id)
|
|
||||||
|
|
||||||
invite_link = await self.telegram_bot.create_chat_invite_link(project.channel.telegram_id, requires_approval)
|
|
||||||
log.info(
|
|
||||||
'Created invite link for project=%s channel_telegram_id=%s requires_approval=%s invite_link=%s',
|
|
||||||
project.id,
|
|
||||||
project.channel.telegram_id,
|
|
||||||
requires_approval,
|
|
||||||
invite_link,
|
|
||||||
)
|
|
||||||
|
|
||||||
placement = domain.Placement(
|
|
||||||
project_id=input.project_id,
|
|
||||||
placement_channel_id=input.placement_channel_id,
|
|
||||||
creative_id=input.creative_id,
|
|
||||||
workspace_id=workspace_id,
|
|
||||||
placement_date=input.placement_date,
|
|
||||||
cost=input.cost,
|
|
||||||
comment=input.comment,
|
|
||||||
invite_link=invite_link,
|
|
||||||
status=domain.PlacementStatus.ACTIVE,
|
|
||||||
)
|
|
||||||
|
|
||||||
await self.database.create_placement(placement)
|
|
||||||
|
|
||||||
return dto.PlacementOutput(
|
|
||||||
id=placement.id,
|
|
||||||
project_id=placement.project_id,
|
|
||||||
project_channel_title=project.channel.title,
|
|
||||||
placement_channel_id=placement.placement_channel_id,
|
|
||||||
placement_channel_title=placement_channel.title,
|
|
||||||
creative_id=placement.creative_id,
|
|
||||||
creative_name=creative.name,
|
|
||||||
placement_date=placement.wanted_placement_date,
|
|
||||||
cost=placement.cost,
|
|
||||||
comment=placement.comment,
|
|
||||||
ad_post_url=None,
|
|
||||||
invite_link_type=project.purchase_invite_type_default,
|
|
||||||
invite_link=placement.invite_link,
|
|
||||||
status=placement.status,
|
|
||||||
subscriptions_count=0,
|
|
||||||
created_at=placement.created_at,
|
|
||||||
)
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import logging
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
from src import domain, dto
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from .. import Usecase
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
async def delete_placement(self: 'Usecase', input: dto.DeletePlacementInput) -> None:
|
|
||||||
context = await self.ensure_workspace_permission(
|
|
||||||
input.workspace_id, input.user_id, domain.PermissionKey.PLACEMENTS_WRITE
|
|
||||||
)
|
|
||||||
|
|
||||||
placement = await self.database.get_placement(input.workspace_id, input.placement_id)
|
|
||||||
if not placement:
|
|
||||||
log.warning('Placement %s not found for user %s', input.placement_id, input.user_id)
|
|
||||||
raise domain.PlacementNotFound(input.placement_id)
|
|
||||||
|
|
||||||
context.ensure_project_permission(domain.PermissionKey.PLACEMENTS_WRITE, placement.project_id)
|
|
||||||
|
|
||||||
await self.database.delete_placement(input.placement_id)
|
|
||||||
@@ -44,12 +44,8 @@ async def fetch_placement_post_cycle(self: 'Usecase', interval_seconds: int) ->
|
|||||||
wanted_placement_date=post.created_at,
|
wanted_placement_date=post.created_at,
|
||||||
cost=None,
|
cost=None,
|
||||||
comment=None,
|
comment=None,
|
||||||
invite_link=purchase_channel.invite_link,
|
|
||||||
workspace_id=purchase_channel.purchase.workspace_id,
|
|
||||||
project_id=purchase_channel.purchase.project_id,
|
project_id=purchase_channel.purchase.project_id,
|
||||||
creative_id=purchase_channel.purchase.creative_id,
|
creative_id=purchase_channel.purchase.creative_id,
|
||||||
placement_channel_id=purchase_channel.channel_id,
|
|
||||||
purchase_id=purchase_channel.purchase_id,
|
|
||||||
purchase_channel_id=purchase_channel.id,
|
purchase_channel_id=purchase_channel.id,
|
||||||
post=post,
|
post=post,
|
||||||
status=domain.PlacementStatus.ACTIVE,
|
status=domain.PlacementStatus.ACTIVE,
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ async def get_placement(self: 'Usecase', input: dto.GetPlacementInput) -> dto.Pl
|
|||||||
context.ensure_project_permission(domain.PermissionKey.PLACEMENTS_READ, placement.project_id)
|
context.ensure_project_permission(domain.PermissionKey.PLACEMENTS_READ, placement.project_id)
|
||||||
|
|
||||||
ad_post_url = placement.post.url if placement.post else None
|
ad_post_url = placement.post.url if placement.post else None
|
||||||
|
purchase_channel = placement.purchase_channel
|
||||||
|
if not purchase_channel or not purchase_channel.channel:
|
||||||
|
log.warning('Placement %s missing purchase channel data', placement.id)
|
||||||
|
raise domain.PurchaseChannelNotFound()
|
||||||
|
|
||||||
subscriptions_count = await self.database.count_subscriptions_by_placement(placement.id)
|
subscriptions_count = await self.database.count_subscriptions_by_placement(placement.id)
|
||||||
|
|
||||||
@@ -29,17 +33,19 @@ async def get_placement(self: 'Usecase', input: dto.GetPlacementInput) -> dto.Pl
|
|||||||
id=placement.id,
|
id=placement.id,
|
||||||
project_id=placement.project_id,
|
project_id=placement.project_id,
|
||||||
project_channel_title=placement.project.channel.title,
|
project_channel_title=placement.project.channel.title,
|
||||||
placement_channel_id=placement.placement_channel_id,
|
placement_channel_id=purchase_channel.channel_id,
|
||||||
placement_channel_title=placement.placement_channel.title,
|
placement_channel_title=purchase_channel.channel.title,
|
||||||
creative_id=placement.creative_id,
|
creative_id=placement.creative_id,
|
||||||
creative_name=placement.creative.name,
|
creative_name=placement.creative.name,
|
||||||
placement_date=placement.wanted_placement_date,
|
placement_date=placement.wanted_placement_date,
|
||||||
cost=placement.cost,
|
cost=placement.cost,
|
||||||
comment=placement.comment,
|
comment=placement.comment,
|
||||||
ad_post_url=ad_post_url,
|
ad_post_url=ad_post_url,
|
||||||
invite_link_type=placement.project.purchase_invite_type_default,
|
invite_link_type=purchase_channel.invite_link_type,
|
||||||
invite_link=placement.invite_link,
|
invite_link=purchase_channel.invite_link,
|
||||||
status=placement.status,
|
status=placement.status,
|
||||||
subscriptions_count=subscriptions_count,
|
subscriptions_count=subscriptions_count,
|
||||||
|
purchase_id=purchase_channel.purchase_id,
|
||||||
|
purchase_channel_id=purchase_channel.id,
|
||||||
created_at=placement.created_at,
|
created_at=placement.created_at,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import logging
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from src import domain, dto
|
from src import domain, dto
|
||||||
@@ -5,6 +6,8 @@ from src import domain, dto
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .. import Usecase
|
from .. import Usecase
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
async def get_placements(self: 'Usecase', input: dto.GetPlacementsInput) -> list[dto.PlacementOutput]:
|
async def get_placements(self: 'Usecase', input: dto.GetPlacementsInput) -> list[dto.PlacementOutput]:
|
||||||
context = await self.ensure_workspace_permission(
|
context = await self.ensure_workspace_permission(
|
||||||
@@ -32,24 +35,30 @@ async def get_placements(self: 'Usecase', input: dto.GetPlacementsInput) -> list
|
|||||||
placement_outputs = []
|
placement_outputs = []
|
||||||
for placement in placements:
|
for placement in placements:
|
||||||
ad_post_url = placement.post.url if placement.post else None
|
ad_post_url = placement.post.url if placement.post else None
|
||||||
|
purchase_channel = placement.purchase_channel
|
||||||
|
if not purchase_channel or not purchase_channel.channel:
|
||||||
|
log.warning('Placement %s missing purchase channel data', placement.id)
|
||||||
|
continue
|
||||||
|
|
||||||
placement_outputs.append(
|
placement_outputs.append(
|
||||||
dto.PlacementOutput(
|
dto.PlacementOutput(
|
||||||
id=placement.id,
|
id=placement.id,
|
||||||
project_id=placement.project_id,
|
project_id=placement.project_id,
|
||||||
project_channel_title=placement.project.channel.title,
|
project_channel_title=placement.project.channel.title,
|
||||||
placement_channel_id=placement.placement_channel_id,
|
placement_channel_id=purchase_channel.channel_id,
|
||||||
placement_channel_title=placement.placement_channel.title,
|
placement_channel_title=purchase_channel.channel.title,
|
||||||
creative_id=placement.creative_id,
|
creative_id=placement.creative_id,
|
||||||
creative_name=placement.creative.name,
|
creative_name=placement.creative.name,
|
||||||
placement_date=placement.wanted_placement_date,
|
placement_date=placement.wanted_placement_date,
|
||||||
cost=placement.cost,
|
cost=placement.cost,
|
||||||
comment=placement.comment,
|
comment=placement.comment,
|
||||||
ad_post_url=ad_post_url,
|
ad_post_url=ad_post_url,
|
||||||
invite_link_type=placement.project.purchase_invite_type_default,
|
invite_link_type=purchase_channel.invite_link_type,
|
||||||
invite_link=placement.invite_link,
|
invite_link=purchase_channel.invite_link,
|
||||||
status=placement.status,
|
status=placement.status,
|
||||||
subscriptions_count=subscriptions_counts.get(placement.id, 0),
|
subscriptions_count=subscriptions_counts.get(placement.id, 0),
|
||||||
|
purchase_id=purchase_channel.purchase_id,
|
||||||
|
purchase_channel_id=purchase_channel.id,
|
||||||
created_at=placement.created_at,
|
created_at=placement.created_at,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
import logging
|
|
||||||
import uuid
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
from src import domain, dto
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from .. import Usecase
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
async def update_placement(
|
|
||||||
self: 'Usecase',
|
|
||||||
placement_id: uuid.UUID,
|
|
||||||
input: dto.UpdatePlacementInput,
|
|
||||||
user_id: uuid.UUID,
|
|
||||||
workspace_id: uuid.UUID,
|
|
||||||
) -> dto.PlacementOutput:
|
|
||||||
context = await self.ensure_workspace_permission(workspace_id, user_id, domain.PermissionKey.PLACEMENTS_WRITE)
|
|
||||||
|
|
||||||
placement = await self.database.get_placement(workspace_id, placement_id)
|
|
||||||
if not placement:
|
|
||||||
log.warning('Placement %s not found for user %s', placement_id, user_id)
|
|
||||||
raise domain.PlacementNotFound(placement_id)
|
|
||||||
|
|
||||||
context.ensure_project_permission(domain.PermissionKey.PLACEMENTS_WRITE, placement.project_id)
|
|
||||||
|
|
||||||
if input.placement_date is not None:
|
|
||||||
placement.wanted_placement_date = input.placement_date
|
|
||||||
if input.cost is not None:
|
|
||||||
placement.cost = input.cost
|
|
||||||
if input.comment is not None:
|
|
||||||
placement.comment = input.comment
|
|
||||||
if input.status is not None:
|
|
||||||
placement.status = input.status
|
|
||||||
|
|
||||||
await self.database.update_placement(placement)
|
|
||||||
|
|
||||||
ad_post_url = placement.post.url if placement.post else None
|
|
||||||
|
|
||||||
subscriptions_count = await self.database.count_subscriptions_by_placement(placement.id)
|
|
||||||
|
|
||||||
return dto.PlacementOutput(
|
|
||||||
id=placement.id,
|
|
||||||
project_id=placement.project_id,
|
|
||||||
project_channel_title=placement.project.channel.title,
|
|
||||||
placement_channel_id=placement.placement_channel_id,
|
|
||||||
placement_channel_title=placement.placement_channel.title,
|
|
||||||
creative_id=placement.creative_id,
|
|
||||||
creative_name=placement.creative.name,
|
|
||||||
placement_date=placement.wanted_placement_date,
|
|
||||||
cost=placement.cost,
|
|
||||||
comment=placement.comment,
|
|
||||||
ad_post_url=ad_post_url,
|
|
||||||
invite_link_type=placement.project.purchase_invite_type_default,
|
|
||||||
invite_link=placement.invite_link,
|
|
||||||
status=placement.status,
|
|
||||||
subscriptions_count=subscriptions_count,
|
|
||||||
created_at=placement.created_at,
|
|
||||||
)
|
|
||||||
@@ -99,7 +99,6 @@ async def create_purchase(
|
|||||||
|
|
||||||
details = input.details
|
details = input.details
|
||||||
purchase = domain.Purchase(
|
purchase = domain.Purchase(
|
||||||
workspace_id=workspace_id,
|
|
||||||
project_id=project.id,
|
project_id=project.id,
|
||||||
creative_id=creative.id,
|
creative_id=creative.id,
|
||||||
placement_at=details.placement_at if details else None,
|
placement_at=details.placement_at if details else None,
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ package screens
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"example.com/m/adapter/backend"
|
||||||
"example.com/m/bot"
|
"example.com/m/bot"
|
||||||
"github.com/NicoNex/echotron/v3"
|
"github.com/NicoNex/echotron/v3"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
@@ -42,6 +44,8 @@ func (s *PurchaseDetails) renderDetails(b *bot.Bot, mode bot.RenderMode, jwt str
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
placementsByChannel := s.loadPlacementsByChannel(b, jwt, purchase)
|
||||||
|
|
||||||
// Получаем информацию о креативе
|
// Получаем информацию о креативе
|
||||||
creative, err := b.Backend.GetCreative(
|
creative, err := b.Backend.GetCreative(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
@@ -54,13 +58,18 @@ func (s *PurchaseDetails) renderDetails(b *bot.Bot, mode bot.RenderMode, jwt str
|
|||||||
creative = nil
|
creative = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
totalPlacements := 0
|
||||||
|
for _, items := range placementsByChannel {
|
||||||
|
totalPlacements += len(items)
|
||||||
|
}
|
||||||
|
|
||||||
text := "<i>… › Закупы › Детали</i>\n\n<b>Детали закупа</b>\n\n"
|
text := "<i>… › Закупы › Детали</i>\n\n<b>Детали закупа</b>\n\n"
|
||||||
|
|
||||||
// Информация о креативе
|
// Информация о креативе
|
||||||
if creative != nil {
|
if creative != nil {
|
||||||
text += fmt.Sprintf("▪ <b>Креатив:</b> %s\n", creative.Name)
|
text += fmt.Sprintf("• <b>Креатив:</b> %s\n", creative.Name)
|
||||||
} else {
|
} else {
|
||||||
text += fmt.Sprintf("▪ <b>Креатив ID:</b> %s\n", purchase.CreativeID)
|
text += fmt.Sprintf("• <b>Креатив ID:</b> %s\n", purchase.CreativeID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Статус закупа
|
// Статус закупа
|
||||||
@@ -70,7 +79,9 @@ func (s *PurchaseDetails) renderDetails(b *bot.Bot, mode bot.RenderMode, jwt str
|
|||||||
statusSymbol = "■"
|
statusSymbol = "■"
|
||||||
statusText = "Архивирован"
|
statusText = "Архивирован"
|
||||||
}
|
}
|
||||||
text += fmt.Sprintf("▪ <b>Статус:</b> %s %s\n\n", statusSymbol, statusText)
|
text += fmt.Sprintf("• <b>Статус:</b> %s %s\n", statusSymbol, statusText)
|
||||||
|
text += fmt.Sprintf("• <b>Каналов:</b> %d\n", len(purchase.Channels))
|
||||||
|
text += fmt.Sprintf("• <b>Размещений:</b> %d\n\n", totalPlacements)
|
||||||
|
|
||||||
// Информация о каналах
|
// Информация о каналах
|
||||||
if len(purchase.Channels) == 0 {
|
if len(purchase.Channels) == 0 {
|
||||||
@@ -79,20 +90,7 @@ func (s *PurchaseDetails) renderDetails(b *bot.Bot, mode bot.RenderMode, jwt str
|
|||||||
text += fmt.Sprintf("<b>Каналы (%d):</b>\n\n", len(purchase.Channels))
|
text += fmt.Sprintf("<b>Каналы (%d):</b>\n\n", len(purchase.Channels))
|
||||||
|
|
||||||
for i, ch := range purchase.Channels {
|
for i, ch := range purchase.Channels {
|
||||||
// Определяем статус
|
chStatusText := formatPurchaseChannelStatus(ch.Status)
|
||||||
statusSymbol := "○"
|
|
||||||
switch ch.Status {
|
|
||||||
case "planned":
|
|
||||||
statusSymbol = "○"
|
|
||||||
case "approved":
|
|
||||||
statusSymbol = "◉"
|
|
||||||
case "rejected":
|
|
||||||
statusSymbol = "⊗"
|
|
||||||
case "in_progress":
|
|
||||||
statusSymbol = "◐"
|
|
||||||
case "completed":
|
|
||||||
statusSymbol = "●"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Название канала
|
// Название канала
|
||||||
channelName := "Без названия"
|
channelName := "Без названия"
|
||||||
@@ -102,16 +100,43 @@ func (s *PurchaseDetails) renderDetails(b *bot.Bot, mode bot.RenderMode, jwt str
|
|||||||
channelName = "@" + *ch.Channel.Username
|
channelName = "@" + *ch.Channel.Username
|
||||||
}
|
}
|
||||||
|
|
||||||
text += fmt.Sprintf("%d. %s <b>%s</b>\n", i+1, statusSymbol, channelName)
|
text += fmt.Sprintf("%d. <b>%s</b>\n", i+1, channelName)
|
||||||
|
text += fmt.Sprintf(" ◦ <i>%s</i>\n", chStatusText)
|
||||||
|
|
||||||
// Стоимость
|
// Стоимость
|
||||||
if ch.Details != nil && ch.Details.Cost != nil {
|
if ch.Details != nil && ch.Details.Cost != nil {
|
||||||
text += fmt.Sprintf(" ▸ Стоимость: %.0f₽\n", ch.Details.Cost.Value)
|
text += fmt.Sprintf(" ◦ Стоимость: %.0f₽\n", ch.Details.Cost.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Дата размещения (план)
|
||||||
|
if ch.Details != nil && ch.Details.PlacementAt != nil && *ch.Details.PlacementAt != "" {
|
||||||
|
text += fmt.Sprintf(" ◦ План: %s\n", *ch.Details.PlacementAt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Комментарий
|
// Комментарий
|
||||||
if ch.Comment != nil && *ch.Comment != "" {
|
if ch.Comment != nil && *ch.Comment != "" {
|
||||||
text += fmt.Sprintf(" ▸ %s\n", *ch.Comment)
|
text += fmt.Sprintf(" ◦ %s\n", *ch.Comment)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Информация о размещении
|
||||||
|
placements := placementsByChannel[ch.ID]
|
||||||
|
if len(placements) == 0 {
|
||||||
|
text += " ◦ Размещение: <b>нет</b>\n"
|
||||||
|
} else {
|
||||||
|
placement := placements[0]
|
||||||
|
text += fmt.Sprintf(" ◦ Размещение: <b>есть</b> (%d)\n", len(placements))
|
||||||
|
if placement.Status != "" {
|
||||||
|
text += fmt.Sprintf(" ◦ Статус размещения: %s\n", formatPlacementStatus(placement.Status))
|
||||||
|
}
|
||||||
|
if placement.PlacementDate != "" {
|
||||||
|
text += fmt.Sprintf(" ◦ Дата: %s\n", placement.PlacementDate)
|
||||||
|
}
|
||||||
|
if placement.AdPostURL != nil && *placement.AdPostURL != "" {
|
||||||
|
text += fmt.Sprintf(" ◦ Пост: %s\n", *placement.AdPostURL)
|
||||||
|
}
|
||||||
|
if placement.SubscriptionsCount > 0 {
|
||||||
|
text += fmt.Sprintf(" ◦ Подписки: %d\n", placement.SubscriptionsCount)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Тип invite link
|
// Тип invite link
|
||||||
@@ -119,7 +144,7 @@ func (s *PurchaseDetails) renderDetails(b *bot.Bot, mode bot.RenderMode, jwt str
|
|||||||
if ch.InviteLinkType == "approval" {
|
if ch.InviteLinkType == "approval" {
|
||||||
inviteLinkTypeText = "с одобрением"
|
inviteLinkTypeText = "с одобрением"
|
||||||
}
|
}
|
||||||
text += fmt.Sprintf(" ▸ Ссылка: %s\n", inviteLinkTypeText)
|
text += fmt.Sprintf(" ◦ Ссылка: %s\n", inviteLinkTypeText)
|
||||||
|
|
||||||
text += "\n"
|
text += "\n"
|
||||||
}
|
}
|
||||||
@@ -136,6 +161,94 @@ func (s *PurchaseDetails) renderDetails(b *bot.Bot, mode bot.RenderMode, jwt str
|
|||||||
b.Render(text, keyboard, mode)
|
b.Render(text, keyboard, mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *PurchaseDetails) loadPlacementsByChannel(
|
||||||
|
b *bot.Bot,
|
||||||
|
jwt string,
|
||||||
|
purchase *backend.Purchase,
|
||||||
|
) map[string][]backend.Placement {
|
||||||
|
placementsByChannel := make(map[string][]backend.Placement)
|
||||||
|
if purchase == nil || purchase.CreativeID == "" || len(purchase.Channels) == 0 {
|
||||||
|
return placementsByChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
projectID := s.ProjectID
|
||||||
|
creativeID := purchase.CreativeID
|
||||||
|
pageSize := 50
|
||||||
|
maxPages := 5
|
||||||
|
foundChannels := make(map[string]bool)
|
||||||
|
|
||||||
|
for page := 1; page <= maxPages; page++ {
|
||||||
|
resp, err := b.Backend.GetPlacements(
|
||||||
|
context.Background(),
|
||||||
|
jwt,
|
||||||
|
s.WorkspaceID,
|
||||||
|
&projectID,
|
||||||
|
nil,
|
||||||
|
&creativeID,
|
||||||
|
false,
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Failed to get placements for purchase details")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, placement := range resp.Items {
|
||||||
|
if placement.PurchaseID == nil || *placement.PurchaseID != purchase.ID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if placement.PurchaseChannelID == nil || *placement.PurchaseChannelID == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
chID := *placement.PurchaseChannelID
|
||||||
|
placementsByChannel[chID] = append(placementsByChannel[chID], placement)
|
||||||
|
foundChannels[chID] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.Pages <= page || len(foundChannels) == len(purchase.Channels) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return placementsByChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatPurchaseChannelStatus(status string) string {
|
||||||
|
switch status {
|
||||||
|
case "planned":
|
||||||
|
return "Планируется"
|
||||||
|
case "approved":
|
||||||
|
return "Согласовано"
|
||||||
|
case "rejected":
|
||||||
|
return "Отклонено"
|
||||||
|
case "in_progress":
|
||||||
|
return "В работе"
|
||||||
|
case "completed":
|
||||||
|
return "Завершено"
|
||||||
|
default:
|
||||||
|
return "Неизвестно"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatPlacementStatus(status string) string {
|
||||||
|
normalized := strings.TrimSpace(strings.ToLower(status))
|
||||||
|
switch normalized {
|
||||||
|
case "planned":
|
||||||
|
return "Планируется"
|
||||||
|
case "approved":
|
||||||
|
return "Согласовано"
|
||||||
|
case "rejected":
|
||||||
|
return "Отклонено"
|
||||||
|
case "in_progress":
|
||||||
|
return "В работе"
|
||||||
|
case "completed":
|
||||||
|
return "Размещено"
|
||||||
|
default:
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *PurchaseDetails) HandleCallback(b *bot.Bot, u *echotron.Update) {
|
func (s *PurchaseDetails) HandleCallback(b *bot.Bot, u *echotron.Update) {
|
||||||
if u.CallbackQuery == nil || u.CallbackQuery.Data == "" {
|
if u.CallbackQuery == nil || u.CallbackQuery.Data == "" {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -1283,9 +1283,13 @@ func (s *PurchaseOptionalDetails) createPurchase(b *bot.Bot, jwt string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
b.SendNew("✅ Закуп создан", bot.Keyboard(
|
b.SetState(&PurchaseDetails{
|
||||||
bot.Row(bot.Button("● Готово", "done")),
|
WorkspaceID: s.WorkspaceID,
|
||||||
))
|
ProjectID: s.ProjectID,
|
||||||
|
ProjectTitle: s.ProjectTitle,
|
||||||
|
PurchaseID: purchase.ID,
|
||||||
|
BackState: s.BackState,
|
||||||
|
}, bot.NewMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PurchaseOptionalDetails) buildPurchaseDetails(purchaseType *string) *backend.PurchaseDetails {
|
func (s *PurchaseOptionalDetails) buildPurchaseDetails(purchaseType *string) *backend.PurchaseDetails {
|
||||||
@@ -1449,5 +1453,15 @@ func (s *PurchaseOptionalDetails) injectInviteLink(text, inviteLink string) stri
|
|||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
re := regexp.MustCompile(`<a\s+class="tg-link">([^<]*)</a>`)
|
re := regexp.MustCompile(`<a\s+class="tg-link">([^<]*)</a>`)
|
||||||
return re.ReplaceAllString(text, fmt.Sprintf(`<a href="%s">$1</a>`, inviteLink))
|
return re.ReplaceAllStringFunc(text, func(match string) string {
|
||||||
|
sub := re.FindStringSubmatch(match)
|
||||||
|
if len(sub) < 2 {
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
inner := strings.TrimSpace(sub[1])
|
||||||
|
if inner == "" {
|
||||||
|
return fmt.Sprintf(`<a href="%s">%s</a>`, inviteLink, inviteLink)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(`<a href="%s">%s</a>`, inviteLink, sub[1])
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -245,22 +245,13 @@ func (s *SelectChannelsForPurchase) createPurchase(b *bot.Bot, jwt string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Показываем успешное создание
|
b.SetState(&PurchaseDetails{
|
||||||
successText := fmt.Sprintf(`
|
WorkspaceID: s.WorkspaceID,
|
||||||
<i>… › Создание › Готово</i>
|
ProjectID: s.ProjectID,
|
||||||
|
ProjectTitle: s.ProjectTitle,
|
||||||
<b>Закуп создан</b>
|
PurchaseID: purchase.ID,
|
||||||
|
BackState: s.BackState,
|
||||||
▪ <b>ID:</b> %s
|
}, bot.EditMessage)
|
||||||
▪ <b>Креатив:</b> %s
|
|
||||||
▪ <b>Каналов:</b> %d
|
|
||||||
|
|
||||||
Закуп создан и готов к работе
|
|
||||||
`, purchase.ID, purchase.CreativeID, len(purchase.Channels))
|
|
||||||
|
|
||||||
b.Edit(successText, bot.Keyboard(
|
|
||||||
bot.Row(bot.Button("● Готово", "done")),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SelectChannelsForPurchase) HandleMessage(b *bot.Bot, u *echotron.Update) {
|
func (s *SelectChannelsForPurchase) HandleMessage(b *bot.Bot, u *echotron.Update) {
|
||||||
|
|||||||
Reference in New Issue
Block a user