diff --git a/backend/api_app/controllers/companies.py b/backend/api_app/controllers/companies.py index 45766f0..dfe5450 100644 --- a/backend/api_app/controllers/companies.py +++ b/backend/api_app/controllers/companies.py @@ -23,8 +23,9 @@ CompanyPlatformOverview, CompanyTypes, ParentCompanyTree, - PlatformCompanies, TopCompanies, + TopCompaniesShort, + TopCompaniesOverviewShort, ) from config import get_logger from dbcon.queries import ( @@ -108,6 +109,47 @@ def get_company_apps_new( ) return results +def make_top_companies(top_df: pd.DataFrame) -> TopCompaniesShort: + top_sdk_df = top_df[top_df["tag_source"] == "sdk"].copy() + top_adstxt_direct_df = top_df[top_df["tag_source"] == "app_ads_direct"].copy() + top_adstxt_reseller_df = top_df[top_df["tag_source"] == "app_ads_reseller"].copy() + + top_sdk_df["company_title"] = np.where( + top_sdk_df["company_name"].isna(), + top_sdk_df["company_domain"], + top_sdk_df["company_name"], + ) + top_adstxt_direct_df["company_title"] = np.where( + top_adstxt_direct_df["company_name"].isna(), + top_adstxt_direct_df["company_domain"], + top_adstxt_direct_df["company_name"], + ) + top_adstxt_reseller_df["company_title"] = np.where( + top_adstxt_reseller_df["company_name"].isna(), + top_adstxt_reseller_df["company_domain"], + top_adstxt_reseller_df["company_name"], + ) + + top_sdk_df = top_sdk_df.rename( + columns={"company_title": "group", "app_count": "value"}, + ).sort_values(by=["value"], ascending=True) + top_adstxt_direct_df = top_adstxt_direct_df.rename( + columns={"company_title": "group", "app_count": "value"}, + ).sort_values(by=["value"], ascending=True) + top_adstxt_reseller_df = top_adstxt_reseller_df.rename( + columns={"company_title": "group", "app_count": "value"}, + ).sort_values(by=["value"], ascending=True) + + top_companies_short = TopCompaniesShort( + sdk=top_sdk_df.to_dict(orient="records"), + adstxt_direct=top_adstxt_direct_df.to_dict(orient="records"), + adstxt_reseller=top_adstxt_reseller_df.to_dict(orient="records"), + ) + return top_companies_short + + + + def get_overviews( category: str | None = None, @@ -140,36 +182,6 @@ def get_overviews( validate="m:1", ) - top_sdk_df = top_df[top_df["tag_source"] == "sdk"].copy() - top_adstxt_direct_df = top_df[top_df["tag_source"] == "app_ads_direct"].copy() - top_adstxt_reseller_df = top_df[top_df["tag_source"] == "app_ads_reseller"].copy() - - top_sdk_df["company_title"] = np.where( - top_sdk_df["company_name"].isna(), - top_sdk_df["company_domain"], - top_sdk_df["company_name"], - ) - top_adstxt_direct_df["company_title"] = np.where( - top_adstxt_direct_df["company_name"].isna(), - top_adstxt_direct_df["company_domain"], - top_adstxt_direct_df["company_name"], - ) - top_adstxt_reseller_df["company_title"] = np.where( - top_adstxt_reseller_df["company_name"].isna(), - top_adstxt_reseller_df["company_domain"], - top_adstxt_reseller_df["company_name"], - ) - - top_sdk_df = top_sdk_df.rename( - columns={"company_title": "group", "app_count": "value"}, - ).sort_values(by=["value"], ascending=True) - top_adstxt_direct_df = top_adstxt_direct_df.rename( - columns={"company_title": "group", "app_count": "value"}, - ).sort_values(by=["value"], ascending=True) - top_adstxt_reseller_df = top_adstxt_reseller_df.rename( - columns={"company_title": "group", "app_count": "value"}, - ).sort_values(by=["value"], ascending=True) - category_overview = make_category_uniques(df=overview_df) overview_df = ( @@ -211,17 +223,11 @@ def get_overviews( ascending=False, ).head(1000) + top_companies_short = make_top_companies(top_df) + results = CompaniesOverview( companies_overview=overview_df.to_dict(orient="records"), - sdk=PlatformCompanies( - top=top_sdk_df.to_dict(orient="records"), - ), - adstxt_direct=PlatformCompanies( - top=top_adstxt_direct_df.to_dict(orient="records"), - ), - adstxt_reseller=PlatformCompanies( - top=top_adstxt_reseller_df.to_dict(orient="records"), - ), + top = top_companies_short, categories=category_overview, ) @@ -262,7 +268,7 @@ def append_overall_categories(df: pd.DataFrame) -> pd.DataFrame: return df -def companies_overview(categories: list[int]) -> TopCompanies: +def old_companies_overview(categories: list[int]) -> TopCompanies: """Process networks and return TopCompanies class.""" df = get_top_companies(categories=categories) df_parents = get_top_companies( @@ -844,7 +850,7 @@ async def top_networks(self: Self) -> TopCompanies: """ logger.info("GET /api/networks start") - overview = companies_overview(categories=[1]) + overview = old_companies_overview(categories=[1]) logger.info("GET /api/networks return") return overview @@ -860,7 +866,7 @@ async def top_trackers(self: Self) -> TopCompanies: """ logger.info("GET /api/trackers start") - overview = companies_overview(categories=[2, 3]) + overview = old_companies_overview(categories=[2, 3]) logger.info("GET /api/trackers return") return overview @@ -902,3 +908,31 @@ async def adtech_type( logger.info(f"/companies/types/{type_slug}?{category=} return") return overview + + @get(path="/companies/topshort/", cache=True) + async def get_companies_shortlist_top(self: Self) -> TopCompaniesOverviewShort: + """Handle GET request for a list of adtech company categories. + + Returns + ------- + A dictionary representation of the list of categories + each with an id, name, type and total of apps + + """ + logger.info(f"{self.path} start") + adnetworks = get_companies_top(type_slug='ad-networks', app_category=None, limit=5) + mmps = get_companies_top(type_slug='ad-attribution', app_category=None, limit=5) + # analytics = get_companies_top(type_slug='analytics', app_category=None, limit=5) + top_ad_networks = make_top_companies(adnetworks) + top_mmps = make_top_companies(mmps) + # top_analytics = make_top_companies(analytics) + logger.info(f"{self.path} return") + + top_companies = TopCompaniesOverviewShort( + adnetworks=top_ad_networks, + attribution=top_mmps, + analytics=list(), + ) + + return top_companies + diff --git a/backend/api_app/models.py b/backend/api_app/models.py index 8780bfd..fcacb93 100644 --- a/backend/api_app/models.py +++ b/backend/api_app/models.py @@ -184,9 +184,24 @@ class CompanyDetail: @dataclass class PlatformCompanies: - """Represents companies for a specific platform.""" + """Companies data for a specific platform (iOS/Android).""" + ios: list[dict] + android: list[dict] + +@dataclass +class TopCompaniesShort: + """Represents top companies across different categories.""" + sdk: PlatformCompanies + adstxt_direct: PlatformCompanies + adstxt_reseller: PlatformCompanies + +@dataclass +class TopCompaniesOverviewShort: + """Represents top companies across different categories.""" + adnetworks: TopCompaniesShort + attribution: TopCompaniesShort + analytics: TopCompaniesShort - top: list[dict] @dataclass @@ -233,9 +248,7 @@ class CompaniesOverview: """ companies_overview: list[CompanyDetail] - sdk: PlatformCompanies - adstxt_direct: PlatformCompanies - adstxt_reseller: PlatformCompanies + top: TopCompaniesShort categories: CategoryOverview @@ -320,3 +333,4 @@ class AppRank: latest: dict history: dict + diff --git a/frontend/src/routes/(newcategorical)/companies/+page.svelte b/frontend/src/routes/(newcategorical)/companies/+page.svelte index eaa9e4b..ef53789 100644 --- a/frontend/src/routes/(newcategorical)/companies/+page.svelte +++ b/frontend/src/routes/(newcategorical)/companies/+page.svelte @@ -72,13 +72,13 @@ {#snippet card2()} {/snippet} {#snippet card3()} diff --git a/frontend/src/routes/(newcategorical)/companies/categories/[category]/+page.svelte b/frontend/src/routes/(newcategorical)/companies/categories/[category]/+page.svelte index 08b2d0c..6a588e9 100644 --- a/frontend/src/routes/(newcategorical)/companies/categories/[category]/+page.svelte +++ b/frontend/src/routes/(newcategorical)/companies/categories/[category]/+page.svelte @@ -46,13 +46,13 @@ {#snippet card2()} - + {/snippet} {#snippet card3()} diff --git a/frontend/src/routes/(newcategorical)/companies/types/[type]/+page.svelte b/frontend/src/routes/(newcategorical)/companies/types/[type]/+page.svelte index fa7ce88..8990837 100644 --- a/frontend/src/routes/(newcategorical)/companies/types/[type]/+page.svelte +++ b/frontend/src/routes/(newcategorical)/companies/types/[type]/+page.svelte @@ -46,9 +46,10 @@ {:then myData} {#if typeof myData == 'string'}

Failed to load company details.

- {:else if myData && myData.categories} + {:else} - + {#snippet card1()} +

Total Ad Tech Companies

@@ -58,17 +59,19 @@

- -
+ {/snippet}
{/if} {:catch error} diff --git a/frontend/src/routes/(newcategorical)/companies/types/[type]/[category]/+page.svelte b/frontend/src/routes/(newcategorical)/companies/types/[type]/[category]/+page.svelte index 25e95d8..2381e3c 100644 --- a/frontend/src/routes/(newcategorical)/companies/types/[type]/[category]/+page.svelte +++ b/frontend/src/routes/(newcategorical)/companies/types/[type]/[category]/+page.svelte @@ -6,8 +6,6 @@ import type { PageData } from './$types'; - import { type CompaniesOverview } from '../../../../../../types'; - interface Props { data: PageData; } diff --git a/frontend/src/routes/+page.server.ts b/frontend/src/routes/+page.server.ts index ec4068f..8850bc8 100644 --- a/frontend/src/routes/+page.server.ts +++ b/frontend/src/routes/+page.server.ts @@ -3,7 +3,7 @@ export const csr: boolean = true; import type { PageServerLoad } from './$types.js'; -export const load: PageServerLoad = async ({ params, setHeaders, url }) => { +export const load: PageServerLoad = async ({ setHeaders }) => { const emptyResponse = {}; setHeaders({ 'cache-control': 'max-age=3600' @@ -14,12 +14,15 @@ export const load: PageServerLoad = async ({ params, setHeaders, url }) => { const androidGameRanks = fetch(`http://localhost:8000/api/rankings/1/1/36/short`); const iOSGameRanks = fetch(`http://localhost:8000/api/rankings/2/4/62/short`); + + const topCompanies = fetch(`http://localhost:8000/api/companies/topshort`); return { androidAppRanks: androidAppRanks.then((resp) => resp.json()), iOSAppRanks: iOSAppRanks.then((resp) => resp.json()), androidGameRanks: androidGameRanks.then((resp) => resp.json()), - iOSGameRanks: iOSGameRanks.then((resp) => resp.json()) + iOSGameRanks: iOSGameRanks.then((resp) => resp.json()), + topCompanies: topCompanies.then((resp) => resp.json()) }; } catch (error) { console.error('Failed to load app data:', error); diff --git a/frontend/src/routes/+page.svelte b/frontend/src/routes/+page.svelte index 8e7ac23..2022f97 100644 --- a/frontend/src/routes/+page.svelte +++ b/frontend/src/routes/+page.svelte @@ -2,6 +2,9 @@ import AppRankTableShort from '$lib/AppRankTableShort.svelte'; import type { HomeData } from '../types'; + import CompaniesBarChart from '$lib/CompaniesBarChart.svelte'; + import WhiteCard from '$lib/WhiteCard.svelte'; + interface Props { data: HomeData; } @@ -152,36 +155,53 @@

- - -
+ \ No newline at end of file diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 99c44cb..b55067a 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -97,6 +97,7 @@ export interface HomeData { iOSAppRanks: Promise<{ ranks: RankedApps[] }>; androidGameRanks: Promise<{ ranks: RankedApps[] }>; iOSGameRanks: Promise<{ ranks: RankedApps[] }>; + topCompanies: TopCompaniesShort ; status?: number; error?: string; } @@ -172,11 +173,21 @@ export interface CompaniesOverviewPlatforms { }[]; } +export interface TopCompaniesOverview { + sdk: CompaniesOverviewPlatforms; + adstxt_direct: CompaniesOverviewPlatforms; + adstxt_reseller: CompaniesOverviewPlatforms; + }; + +export interface TopCompaniesShort { + adnetworks: TopCompaniesOverview; + attribution: TopCompaniesOverview; + analytics: TopCompaniesOverview; +} + export interface CompaniesOverviewSections { companies_overview: CompaniesOverviewEntries[]; - sdk: CompaniesOverviewPlatforms; - adstxt_direct: CompaniesOverviewPlatforms; - adstxt_reseller: CompaniesOverviewPlatforms; + top: TopCompaniesOverview; categories: CompanyCategoryOverview; }