"""广告收益报表的环境与业务代码位口径。""" from __future__ import annotations from datetime import UTC, datetime from sqlalchemy import delete from app.admin.repositories import ad_revenue from app.db.session import SessionLocal from app.models.ad_ecpm import AdEcpmRecord from app.models.ad_feed_reward import AdFeedRewardRecord from app.models.ad_pangle_revenue import AdPangleDailyRevenue from app.models.ad_reward import AdRewardRecord from app.models.user import User REPORT_DATE = "2040-02-03" PLAYBACK_DATE = "2040-02-04" def test_business_scope_filters_client_and_pangle_by_env_and_code(monkeypatch) -> None: db = SessionLocal() try: user = User( phone="18800009991", username="29999999991", register_channel="sms", ) db.add(user) db.flush() db.add_all([ AdEcpmRecord( user_id=user.id, ad_type="reward_video", ad_session_id="scope-prod-business", app_env="prod", our_code_id="prod-reward", ecpm_raw="10000", report_date=REPORT_DATE, created_at=datetime(2040, 2, 3, tzinfo=UTC), ), AdEcpmRecord( user_id=user.id, ad_type="draw", ad_session_id="scope-prod-demo", app_env="prod", our_code_id="prod-demo", ecpm_raw="20000", report_date=REPORT_DATE, created_at=datetime(2040, 2, 3, tzinfo=UTC), ), AdEcpmRecord( user_id=user.id, ad_type="draw", ad_session_id="scope-prod-known-business", app_env="prod", our_code_id="104098712", ecpm_raw="40000", report_date=REPORT_DATE, created_at=datetime(2040, 2, 3, tzinfo=UTC), ), AdEcpmRecord( user_id=user.id, ad_type="reward_video", ad_session_id="scope-test-business", app_env="test", our_code_id="104127529", ecpm_raw="30000", report_date=REPORT_DATE, created_at=datetime(2040, 2, 3, tzinfo=UTC), ), AdPangleDailyRevenue( report_date=REPORT_DATE, app_env="prod", our_code_id="prod-reward", adn="", revenue_yuan=1.5, api_revenue_yuan=1.2, impressions=10, ), AdPangleDailyRevenue( report_date=REPORT_DATE, app_env="prod", our_code_id="prod-demo", adn="", revenue_yuan=8.0, api_revenue_yuan=7.0, impressions=40, ), AdPangleDailyRevenue( report_date=REPORT_DATE, app_env="prod", our_code_id="104098712", adn="", revenue_yuan=2.5, api_revenue_yuan=2.0, impressions=20, ), AdPangleDailyRevenue( report_date=REPORT_DATE, app_env="test", our_code_id="104127529", adn="", revenue_yuan=9.0, api_revenue_yuan=8.0, impressions=50, ), ]) db.commit() monkeypatch.setattr( ad_revenue.app_config, "get_ad_config", lambda _db: { "reward_code_id": "prod-reward", "compare_draw_code_id": "prod-draw", "coupon_draw_code_id": "prod-draw", }, ) business = ad_revenue.ad_revenue_report( db, date_from=REPORT_DATE, date_to=REPORT_DATE, app_env="prod", revenue_scope="business", ) assert business["total_impressions"] == 2 assert business["total_revenue_yuan"] == 0.5 assert business["total_pangle_revenue_yuan"] == 4.0 assert business["total_pangle_api_revenue_yuan"] == 3.2 all_codes = ad_revenue.ad_revenue_report( db, date_from=REPORT_DATE, date_to=REPORT_DATE, app_env="prod", revenue_scope="all", ) assert all_codes["total_impressions"] == 3 assert all_codes["total_revenue_yuan"] == 0.7 assert all_codes["total_pangle_revenue_yuan"] == 12.0 assert all_codes["total_pangle_api_revenue_yuan"] == 10.2 test_business = ad_revenue.ad_revenue_report( db, date_from=REPORT_DATE, date_to=REPORT_DATE, app_env="test", revenue_scope="business", ) assert test_business["total_impressions"] == 1 assert test_business["total_revenue_yuan"] == 0.3 assert test_business["total_pangle_revenue_yuan"] == 9.0 assert test_business["total_pangle_api_revenue_yuan"] == 8.0 all_env_business = ad_revenue.ad_revenue_report( db, date_from=REPORT_DATE, date_to=REPORT_DATE, app_env=None, revenue_scope="business", ) assert all_env_business["total_impressions"] == 3 assert all_env_business["total_revenue_yuan"] == 0.8 assert all_env_business["total_pangle_revenue_yuan"] == 13.0 assert all_env_business["total_pangle_api_revenue_yuan"] == 11.2 finally: db.rollback() db.execute(delete(AdPangleDailyRevenue).where(AdPangleDailyRevenue.report_date == REPORT_DATE)) db.execute(delete(AdEcpmRecord).where(AdEcpmRecord.report_date == REPORT_DATE)) db.execute(delete(User).where(User.phone == "18800009991")) db.commit() db.close() def test_reward_video_incomplete_playback_has_zero_revenue() -> None: db = SessionLocal() phone = "18800009992" sessions = { "closed_early": "rv-zero-closed", "too_short": "rv-zero-short", "capped": "rv-keep-capped", "granted": "rv-keep-granted", } try: user = User(phone=phone, username="29999999992", register_channel="sms") db.add(user) db.flush() for index, (status, session_id) in enumerate(sessions.items(), start=1): created_at = datetime(2040, 2, 4, index, tzinfo=UTC) db.add(AdEcpmRecord( user_id=user.id, ad_type="reward_video", ad_session_id=session_id, app_env="prod", our_code_id="prod-reward", ecpm_raw="10000", report_date=PLAYBACK_DATE, created_at=created_at, )) db.add(AdRewardRecord( trans_id=f"{session_id}-trans", user_id=user.id, coin=0, status=status, reward_scene="reward_video", ad_session_id=session_id, app_env="prod", our_code_id="prod-reward", ecpm_raw="10000", reward_date=PLAYBACK_DATE, created_at=created_at, )) db.commit() result = ad_revenue.ad_revenue_report( db, date_from=PLAYBACK_DATE, date_to=PLAYBACK_DATE, user_id=user.id, ad_type="reward_video", app_env="prod", revenue_scope="all", granularity="hour", ) revenue_by_status = {row["status"]: row["revenue_yuan"] for row in result["items"]} assert revenue_by_status == { "closed_early": 0.0, "too_short": 0.0, "capped": 0.1, "granted": 0.1, } assert result["total_impressions"] == 4 assert result["total_revenue_yuan"] == 0.2 assert len(result["daily"]) == 1 assert result["daily"][0]["date"] == PLAYBACK_DATE assert result["daily"][0]["impressions"] == 4 assert result["daily"][0]["revenue_yuan"] == 0.2 assert sum(row["revenue_yuan"] for row in result["hourly"]) == 0.2 assert result["type_stats"]["reward_video"] == { "impressions": 4, "revenue_yuan": 0.2, } finally: db.rollback() db.execute(delete(AdRewardRecord).where(AdRewardRecord.reward_date == PLAYBACK_DATE)) db.execute(delete(AdEcpmRecord).where(AdEcpmRecord.report_date == PLAYBACK_DATE)) db.execute(delete(User).where(User.phone == phone)) db.commit() db.close() def test_business_category_ecpm_merges_draw_feed_and_both_video_types() -> None: db = SessionLocal() phone = "18800009993" category_date = "2040-02-05" try: user = User(phone=phone, username="29999999993", register_channel="sms") db.add(user) db.flush() db.add_all([ AdEcpmRecord( user_id=user.id, ad_type="draw", ad_session_id="category-draw", app_env="prod", our_code_id="prod-draw", ecpm_raw="10000", report_date=category_date, created_at=datetime(2040, 2, 5, 1, tzinfo=UTC), ), AdEcpmRecord( user_id=user.id, ad_type="feed", ad_session_id="category-feed", app_env="prod", our_code_id="prod-draw", ecpm_raw="20000", report_date=category_date, created_at=datetime(2040, 2, 5, 2, tzinfo=UTC), ), AdEcpmRecord( user_id=user.id, ad_type="reward_video", ad_session_id="category-reward", app_env="prod", our_code_id="prod-reward", ecpm_raw="30000", report_date=category_date, created_at=datetime(2040, 2, 5, 3, tzinfo=UTC), ), AdEcpmRecord( user_id=user.id, ad_type="withdrawal_video", ad_session_id="category-withdraw", app_env="prod", our_code_id="prod-reward", ecpm_raw="50000", report_date=category_date, created_at=datetime(2040, 2, 5, 4, tzinfo=UTC), ), ]) db.commit() result = ad_revenue.ad_revenue_report( db, date_from=category_date, date_to=category_date, user_id=user.id, app_env="prod", revenue_scope="all", ) assert result["category_stats"] == { "draw": {"impressions": 2, "revenue_yuan": 0.3, "ecpm_yuan": 150.0}, "video": {"impressions": 2, "revenue_yuan": 0.8, "ecpm_yuan": 400.0}, } finally: db.rollback() db.execute(delete(AdEcpmRecord).where(AdEcpmRecord.report_date == category_date)) db.execute(delete(User).where(User.phone == phone)) db.commit() db.close() def test_feed_reward_details_keep_each_record_adn() -> None: db = SessionLocal() phone = "18800009994" detail_date = "2040-02-06" try: user = User(phone=phone, username="29999999994", register_channel="sms") db.add(user) db.flush() db.add_all([ AdFeedRewardRecord( client_event_id="detail-adn-pangle", user_id=user.id, reward_date=detail_date, duration_seconds=20, unit_count=1, ecpm_raw="12000", adn="pangle", slot_id="rit-pangle", ad_type="draw", feed_scene="coupon", trace_id="detail-adn-trace", app_env="prod", our_code_id="104098712", coin=12, status="granted", created_at=datetime(2040, 2, 6, 1, tzinfo=UTC), ), AdFeedRewardRecord( client_event_id="detail-adn-gdt", user_id=user.id, reward_date=detail_date, duration_seconds=20, unit_count=1, ecpm_raw="25000", adn="gdt", slot_id="rit-gdt", ad_type="draw", feed_scene="coupon", trace_id="detail-adn-trace", app_env="prod", our_code_id="104098712", coin=25, status="granted", created_at=datetime(2040, 2, 6, 2, tzinfo=UTC), ), ]) db.commit() result = ad_revenue.ad_revenue_report( db, date_from=detail_date, date_to=detail_date, user_id=user.id, app_env="prod", revenue_scope="all", ) item = next(row for row in result["items"] if row["event_key"].startswith("feedgrp-")) assert [detail["adn"] for detail in item["sub_rewards"]] == ["pangle", "gdt"] assert [detail["slot_id"] for detail in item["sub_rewards"]] == ["rit-pangle", "rit-gdt"] finally: db.rollback() db.execute(delete(AdFeedRewardRecord).where(AdFeedRewardRecord.reward_date == detail_date)) db.execute(delete(User).where(User.phone == phone)) db.commit() db.close() def test_feed_reward_source_fallback_requires_unique_trace_and_ecpm() -> None: db = SessionLocal() phone = "18800009995" fallback_date = "2040-02-07" try: user = User(phone=phone, username="29999999995", register_channel="sms") db.add(user) db.flush() db.add_all([ AdFeedRewardRecord( client_event_id="source-fallback-unique", user_id=user.id, reward_date=fallback_date, duration_seconds=3, unit_count=0, ecpm_raw="4700", ad_session_id="flow-session", trace_id="source-trace", app_env="prod", our_code_id="104098712", coin=0, status="too_short", ad_type="draw", feed_scene="comparison", created_at=datetime(2040, 2, 7, 1, tzinfo=UTC), ), AdFeedRewardRecord( client_event_id="source-fallback-ambiguous", user_id=user.id, reward_date=fallback_date, duration_seconds=3, unit_count=0, ecpm_raw="4800", ad_session_id="flow-session", trace_id="source-trace", app_env="prod", our_code_id="104098712", coin=0, status="too_short", ad_type="draw", feed_scene="comparison", created_at=datetime(2040, 2, 7, 2, tzinfo=UTC), ), AdEcpmRecord( user_id=user.id, ad_type="draw", ad_session_id="impression-unique", trace_id="source-trace", ecpm_raw="4700", adn="baidu", slot_id="rit-baidu", app_env="prod", our_code_id="104098712", report_date=fallback_date, created_at=datetime(2040, 2, 7, 1, tzinfo=UTC), ), AdEcpmRecord( user_id=user.id, ad_type="draw", ad_session_id="impression-ambiguous-a", trace_id="source-trace", ecpm_raw="4800", adn="baidu", slot_id="rit-baidu", app_env="prod", our_code_id="104098712", report_date=fallback_date, created_at=datetime(2040, 2, 7, 2, tzinfo=UTC), ), AdEcpmRecord( user_id=user.id, ad_type="draw", ad_session_id="impression-ambiguous-b", trace_id="source-trace", ecpm_raw="4800", adn="ks", slot_id="rit-ks", app_env="prod", our_code_id="104098712", report_date=fallback_date, created_at=datetime(2040, 2, 7, 2, 1, tzinfo=UTC), ), ]) db.commit() result = ad_revenue.ad_revenue_report( db, date_from=fallback_date, date_to=fallback_date, user_id=user.id, app_env="prod", revenue_scope="all", ) item = next(row for row in result["items"] if row["event_key"].startswith("feedgrp-")) details = {detail["ecpm"]: detail for detail in item["sub_rewards"]} assert details["4700"]["adn"] == "baidu" assert details["4700"]["slot_id"] == "rit-baidu" assert details["4800"]["adn"] is None assert details["4800"]["slot_id"] is None finally: db.rollback() db.execute(delete(AdFeedRewardRecord).where(AdFeedRewardRecord.reward_date == fallback_date)) db.execute(delete(AdEcpmRecord).where(AdEcpmRecord.report_date == fallback_date)) db.execute(delete(User).where(User.phone == phone)) db.commit() db.close()