import pytest
from fansifter_common.auth.account import Account
from ows_text_campaigns.api.schemas import AgeRange, Gender
from ows_text_campaigns.campaigns.dtos import (
CampaignAnalyticsLocation,
)
from ows_text_campaigns.campaigns.enums import LinkType
from ows_text_campaigns.campaigns.handlers import (
GetCampaignLinkAnalyticsHandler,
GetCampaignLinkAnalyticsRequest,
)
from ows_text_campaigns.campaigns.models import (
Campaign,
CampaignSendsCountryAggregated,
CampaignUrlAttributes,
TextAnalyticsBenchmarkByAccountArtist,
UrlShortenerClicksCountryAggregated,
UrlShortenerClicksGenderAgeAggregated,
)
from tests.unit.types import CreateModel
class TestGetCampaignLinkAnalyticsHandler:
@pytest.mark.db
def test_get_campaign_link_analytics_empty(
self,
handler: GetCampaignLinkAnalyticsHandler,
create_model: CreateModel,
identity_id: str,
account: Account,
) -> None:
campaign = create_model(
Campaign,
vendor_id=account.vendor_id,
subaccount_id=account.subaccount_id,
)
result = handler.handle(
GetCampaignLinkAnalyticsRequest(
identity_id=identity_id,
campaign_id=campaign.id,
vendor_id=account.vendor_id,
subaccount_id=account.subaccount_id,
)
)
assert result == []
@pytest.mark.db
def test_get_campaign_link_analytics_no_links(
self,
handler: GetCampaignLinkAnalyticsHandler,
create_model: CreateModel,
identity_id: str,
account: Account,
) -> None:
campaign = create_model(
Campaign,
vendor_id=account.vendor_id,
subaccount_id=account.subaccount_id,
content="Hello, world!",
)
create_model(
CampaignSendsCountryAggregated,
campaign_id=campaign.id,
)
result = handler.handle(
GetCampaignLinkAnalyticsRequest(
identity_id=identity_id,
campaign_id=campaign.id,
vendor_id=account.vendor_id,
subaccount_id=account.subaccount_id,
)
)
assert result == []
@pytest.mark.db
def test_get_campaign_link_analytics_no_clicks(
self,
handler: GetCampaignLinkAnalyticsHandler,
create_model: CreateModel,
identity_id: str,
account: Account,
) -> None:
url = "https://example.com/"
campaign = create_model(
Campaign,
vendor_id=account.vendor_id,
subaccount_id=account.subaccount_id,
content=f"""
short_link
""",
)
create_model(
CampaignSendsCountryAggregated,
campaign_id=campaign.id,
sends=10,
country_code="US",
)
result = handler.handle(
GetCampaignLinkAnalyticsRequest(
identity_id=identity_id,
campaign_id=campaign.id,
vendor_id=account.vendor_id,
subaccount_id=account.subaccount_id,
)
)
assert len(result) == 1
url_analytics = result[0]
assert url_analytics.url == url
assert url_analytics.link_type == LinkType.PERSONALIZED
assert url_analytics.total_sends == 10
assert url_analytics.total_clicks == 0
assert url_analytics.clickthrough_rate == 0
assert url_analytics.clickthrough_rate_change is None
assert url_analytics.demographics is not None
assert url_analytics.locations is not None
assert len(url_analytics.locations) == 1
assert url_analytics.locations[0] == CampaignAnalyticsLocation(
country_code="US",
sends=10,
clicks=0,
)
@pytest.mark.db
def test_get_campaign_link_analytics_personalized_link(
self,
handler: GetCampaignLinkAnalyticsHandler,
create_model: CreateModel,
identity_id: str,
account: Account,
) -> None:
url = "https://example.com/"
campaign = create_model(
Campaign,
vendor_id=account.vendor_id,
subaccount_id=account.subaccount_id,
content=f"""
short_link
""",
)
create_model(
CampaignSendsCountryAggregated,
campaign_id=campaign.id,
sends=10,
country_code="US",
)
create_model(
CampaignSendsCountryAggregated,
campaign_id=campaign.id,
sends=10,
country_code="DE",
)
create_model(
UrlShortenerClicksGenderAgeAggregated,
campaign_id=campaign.id,
redirect_url=url,
gender=Gender.MALE,
age_range=AgeRange.FROM_18_TO_22,
clicks=1,
)
create_model(
UrlShortenerClicksGenderAgeAggregated,
campaign_id=campaign.id,
redirect_url=url,
gender=Gender.FEMALE,
age_range=AgeRange.FROM_23_TO_30,
clicks=2,
)
create_model(
UrlShortenerClicksCountryAggregated,
campaign_id=campaign.id,
redirect_url=url,
country_code="US",
clicks=2,
)
create_model(
UrlShortenerClicksCountryAggregated,
campaign_id=campaign.id,
redirect_url=url,
country_code="DE",
clicks=1,
)
create_model(
CampaignUrlAttributes,
campaign_id=campaign.id,
redirect_url=url,
is_personalized=True,
)
result = handler.handle(
GetCampaignLinkAnalyticsRequest(
identity_id=identity_id,
campaign_id=campaign.id,
vendor_id=account.vendor_id,
subaccount_id=account.subaccount_id,
)
)
assert len(result) == 1
url_analytics = result[0]
assert url_analytics.url == url
assert url_analytics.link_type == LinkType.PERSONALIZED
assert url_analytics.total_sends == 20
assert url_analytics.total_clicks == 3
assert url_analytics.clickthrough_rate == 0.15
assert url_analytics.clickthrough_rate_change is None
assert url_analytics.demographics is not None
assert url_analytics.demographics.available_fans_share == 1
# 4 because we generate objects for all genders even if there are no clicks
assert len(url_analytics.demographics.data) == 4
demographics_male = url_analytics.demographics.data[0]
assert demographics_male.id == Gender.MALE
assert demographics_male.total == 1
assert demographics_male.items is not None
assert demographics_male.items[1].label == AgeRange.FROM_18_TO_22
assert demographics_male.items[1].value == 1
demographics_female = url_analytics.demographics.data[1]
assert demographics_female.id == Gender.FEMALE
assert demographics_female.total == 2
assert demographics_female.items is not None
assert demographics_female.items[2].label == AgeRange.FROM_23_TO_30
assert demographics_female.items[2].value == 2
assert url_analytics.locations is not None
assert len(url_analytics.locations) == 2
assert url_analytics.locations[0] == CampaignAnalyticsLocation(
country_code="US",
sends=10,
clicks=2,
)
assert url_analytics.locations[1] == CampaignAnalyticsLocation(
country_code="DE",
sends=10,
clicks=1,
)
@pytest.mark.db
def test_get_campaign_link_analytics_non_personalized_link(
self,
handler: GetCampaignLinkAnalyticsHandler,
create_model: CreateModel,
identity_id: str,
account: Account,
) -> None:
url = "https://example.com/"
campaign = create_model(
Campaign,
vendor_id=account.vendor_id,
subaccount_id=account.subaccount_id,
content=f"""
short_link
""",
)
create_model(
CampaignSendsCountryAggregated,
campaign_id=campaign.id,
sends=10,
country_code="US",
)
create_model(
CampaignSendsCountryAggregated,
campaign_id=campaign.id,
sends=10,
country_code="DE",
)
create_model(
UrlShortenerClicksGenderAgeAggregated,
campaign_id=campaign.id,
redirect_url=url,
gender=None,
age_range=None,
clicks=3,
)
create_model(
UrlShortenerClicksCountryAggregated,
campaign_id=campaign.id,
redirect_url=url,
country_code="US",
clicks=2,
)
create_model(
UrlShortenerClicksCountryAggregated,
campaign_id=campaign.id,
redirect_url=url,
country_code="DE",
clicks=1,
)
create_model(
CampaignUrlAttributes,
campaign_id=campaign.id,
redirect_url=url,
is_personalized=False,
)
result = handler.handle(
GetCampaignLinkAnalyticsRequest(
identity_id=identity_id,
campaign_id=campaign.id,
vendor_id=account.vendor_id,
subaccount_id=account.subaccount_id,
)
)
assert len(result) == 1
url_analytics = result[0]
assert url_analytics.url == url
assert url_analytics.link_type == LinkType.STANDARD
assert url_analytics.total_sends == 20
assert url_analytics.total_clicks == 3
assert url_analytics.clickthrough_rate == 0.15
assert url_analytics.clickthrough_rate_change is None
assert url_analytics.demographics is None
assert url_analytics.locations is not None
assert len(url_analytics.locations) == 2
assert url_analytics.locations[0] == CampaignAnalyticsLocation(
country_code="US",
sends=10,
clicks=2,
)
assert url_analytics.locations[1] == CampaignAnalyticsLocation(
country_code="DE",
sends=10,
clicks=1,
)
@pytest.mark.db
def test_get_campaign_link_analytics_long_link(
self,
handler: GetCampaignLinkAnalyticsHandler,
create_model: CreateModel,
identity_id: str,
account: Account,
) -> None:
url = "https://example.com/"
campaign = create_model(
Campaign,
vendor_id=account.vendor_id,
subaccount_id=account.subaccount_id,
content=f'long_link',
)
create_model(
CampaignSendsCountryAggregated,
campaign_id=campaign.id,
sends=10,
country_code="US",
)
result = handler.handle(
GetCampaignLinkAnalyticsRequest(
identity_id=identity_id,
campaign_id=campaign.id,
vendor_id=account.vendor_id,
subaccount_id=account.subaccount_id,
)
)
assert len(result) == 1
url_analytics = result[0]
assert url_analytics.url == url
assert url_analytics.link_type == LinkType.LONG
assert url_analytics.total_sends == 10
assert url_analytics.total_clicks is None
assert url_analytics.clickthrough_rate is None
assert url_analytics.clickthrough_rate_change is None
assert url_analytics.demographics is None
assert url_analytics.locations is not None
assert len(url_analytics.locations) == 1
assert url_analytics.locations[0] == CampaignAnalyticsLocation(
country_code="US",
sends=10,
clicks=None,
)
@pytest.mark.db
def test_get_campaign_link_analytics_multiple_links(
self,
handler: GetCampaignLinkAnalyticsHandler,
create_model: CreateModel,
identity_id: str,
account: Account,
) -> None:
url_1 = "https://example.com/1"
url_2 = "https://example.com/2"
campaign = create_model(
Campaign,
vendor_id=account.vendor_id,
subaccount_id=account.subaccount_id,
content=f"""
short_link_1
short_link_2
""",
)
create_model(
CampaignSendsCountryAggregated,
campaign_id=campaign.id,
sends=10,
country_code="US",
)
create_model(
UrlShortenerClicksGenderAgeAggregated,
campaign_id=campaign.id,
redirect_url=url_1,
gender=Gender.MALE,
age_range=AgeRange.FROM_18_TO_22,
clicks=1,
)
create_model(
UrlShortenerClicksGenderAgeAggregated,
campaign_id=campaign.id,
redirect_url=url_2,
gender=Gender.FEMALE,
age_range=AgeRange.FROM_23_TO_30,
clicks=2,
)
create_model(
UrlShortenerClicksGenderAgeAggregated,
campaign_id=campaign.id,
redirect_url=url_2,
gender=Gender.MALE,
age_range=None,
clicks=2,
)
create_model(
UrlShortenerClicksCountryAggregated,
campaign_id=campaign.id,
redirect_url=url_1,
country_code="US",
clicks=1,
)
create_model(
UrlShortenerClicksCountryAggregated,
campaign_id=campaign.id,
redirect_url=url_2,
country_code="US",
clicks=4,
)
create_model(
CampaignUrlAttributes,
campaign_id=campaign.id,
redirect_url=url_1,
is_personalized=True,
)
create_model(
CampaignUrlAttributes,
campaign_id=campaign.id,
redirect_url=url_2,
is_personalized=True,
)
result = handler.handle(
GetCampaignLinkAnalyticsRequest(
identity_id=identity_id,
campaign_id=campaign.id,
vendor_id=account.vendor_id,
subaccount_id=account.subaccount_id,
)
)
assert len(result) == 2
url_1_analytics = result[0]
assert url_1_analytics.url == url_1
assert url_1_analytics.link_type == LinkType.PERSONALIZED
assert url_1_analytics.total_sends == 10
assert url_1_analytics.total_clicks == 1
assert url_1_analytics.clickthrough_rate == 0.1
assert url_1_analytics.clickthrough_rate_change is None
assert url_1_analytics.demographics is not None
assert url_1_analytics.demographics.available_fans_share == 1
# 4 because we generate objects for all genders even if there are no clicks
assert len(url_1_analytics.demographics.data) == 4
demographics_male = url_1_analytics.demographics.data[0]
assert demographics_male.id == Gender.MALE
assert demographics_male.total == 1
assert demographics_male.items is not None
assert demographics_male.items[1].label == AgeRange.FROM_18_TO_22
assert demographics_male.items[1].value == 1
assert url_1_analytics.locations is not None
assert len(url_1_analytics.locations) == 1
assert url_1_analytics.locations[0] == CampaignAnalyticsLocation(
country_code="US",
sends=10,
clicks=1,
)
url_2_analytics = result[1]
assert url_2_analytics.url == url_2
assert url_2_analytics.link_type == LinkType.PERSONALIZED
assert url_2_analytics.total_sends == 10
assert url_2_analytics.total_clicks == 4
assert url_2_analytics.clickthrough_rate == 0.4
assert url_2_analytics.clickthrough_rate_change is None
assert url_2_analytics.demographics is not None
assert url_2_analytics.demographics.available_fans_share == 0.5
# 4 because we generate objects for all genders even if there are no clicks
assert len(url_2_analytics.demographics.data) == 4
demographics_female = url_2_analytics.demographics.data[1]
assert demographics_female.id == Gender.FEMALE
assert demographics_female.total == 2
assert demographics_female.items is not None
assert demographics_female.items[2].label == AgeRange.FROM_23_TO_30
assert demographics_female.items[2].value == 2
assert url_2_analytics.locations is not None
assert len(url_2_analytics.locations) == 1
assert url_2_analytics.locations[0] == CampaignAnalyticsLocation(
country_code="US",
sends=10,
clicks=4,
)
@pytest.mark.db
def test_get_campaign_link_analytics_rate_change(
self,
handler: GetCampaignLinkAnalyticsHandler,
create_model: CreateModel,
identity_id: str,
account: Account,
) -> None:
url = "https://example.com/"
campaign = create_model(
Campaign,
vendor_id=account.vendor_id,
subaccount_id=account.subaccount_id,
content=f"""
short_link
""",
)
create_model(
CampaignSendsCountryAggregated,
campaign_id=campaign.id,
sends=10,
country_code="US",
)
create_model(
UrlShortenerClicksGenderAgeAggregated,
campaign_id=campaign.id,
redirect_url=url,
gender=Gender.MALE,
age_range=AgeRange.FROM_18_TO_22,
clicks=5,
)
create_model(
UrlShortenerClicksCountryAggregated,
campaign_id=campaign.id,
redirect_url=url,
country_code="US",
clicks=5,
)
create_model(
CampaignUrlAttributes,
campaign_id=campaign.id,
redirect_url=url,
is_personalized=True,
)
create_model(
TextAnalyticsBenchmarkByAccountArtist,
vendor_id=account.vendor_id,
subaccount_id=account.subaccount_id,
global_participant_id=campaign.global_participant_id,
avg_click_through_rate=1,
)
result = handler.handle(
GetCampaignLinkAnalyticsRequest(
identity_id=identity_id,
campaign_id=campaign.id,
vendor_id=account.vendor_id,
subaccount_id=account.subaccount_id,
)
)
assert len(result) == 1
url_analytics = result[0]
assert url_analytics.clickthrough_rate == 0.5
assert url_analytics.clickthrough_rate_change == -0.5