11defmodule  Algora.Analytics  do 
22  @ moduledoc  false 
3-   def  get_company_analytics ( period  \\  "30d" )  do 
3+   import  Ecto.Query 
4+ 
5+   alias  Algora.Accounts.User 
6+   alias  Algora.Contracts.Contract 
7+   alias  Algora.Repo 
8+ 
9+   require  Algora.SQL 
10+ 
11+   def  get_company_analytics ( period  \\  "30d" ,  from  \\  DateTime . utc_now ( ) )  do 
412    days  =  period  |>  String . replace ( "d" ,  "" )  |>  String . to_integer ( ) 
5-     _since  =  DateTime . add ( DateTime . utc_now ( ) ,  - days  *  24  *  3600 ) 
13+     period_start  =  DateTime . add ( from ,  - days  *  24  *  3600 ) 
14+     previous_period_start  =  DateTime . add ( period_start ,  - days  *  24  *  3600 ) 
615
7-     # Mock data for demonstration 
8-     % { 
9-       total_companies:  150 , 
10-       companies_change:  12 , 
11-       companies_trend:  :up , 
12-       active_companies:  85 , 
13-       active_change:  5 , 
14-       active_trend:  :up , 
15-       avg_time_to_fill:  4.2 , 
16-       time_to_fill_change:  - 0.8 , 
17-       time_to_fill_trend:  :down , 
18-       contract_success_rate:  92 , 
19-       success_rate_change:  2 , 
20-       success_rate_trend:  :up , 
21-       companies:  mock_companies ( ) 
22-     } 
16+     orgs_query  = 
17+       from  u  in  User , 
18+         where:  u . type  ==  :organization , 
19+         select:  % { 
20+           count_all:  count ( u . id ) , 
21+           count_current:  u . id  |>  count ( )  |>  filter ( u . inserted_at  <=  ^ from  and  u . inserted_at  >=  ^ period_start ) , 
22+           count_previous: 
23+              u . id  |>  count ( )  |>  filter ( u . inserted_at  <=  ^ period_start  and  u . inserted_at  >=  ^ previous_period_start ) , 
24+           active_all:  u . id  |>  count ( )  |>  filter ( u . seeded  and  u . activated ) , 
25+           active_current: 
26+              u . id 
27+             |>  count ( ) 
28+             |>  filter ( u . seeded  and  u . activated  and  u . inserted_at  <=  ^ from  and  u . inserted_at  >=  ^ period_start ) , 
29+           active_previous: 
30+              u . id 
31+             |>  count ( ) 
32+             |>  filter ( 
33+               u . seeded  and  u . activated  and  u . inserted_at  <=  ^ period_start  and  u . inserted_at  >=  ^ previous_period_start 
34+             ) 
35+         } 
36+ 
37+     contracts_query  = 
38+       from  u  in  Contract , 
39+         where:  u . inserted_at  >=  ^ previous_period_start , 
40+         select:  % { 
41+           count_current:  u . id  |>  count ( )  |>  filter ( u . inserted_at  <  ^ from  and  u . inserted_at  >=  ^ period_start ) , 
42+           count_previous: 
43+              u . id  |>  count ( )  |>  filter ( u . inserted_at  <  ^ period_start  and  u . inserted_at  >=  ^ previous_period_start ) , 
44+           success_current: 
45+              u . id 
46+             |>  count ( ) 
47+             |>  filter ( 
48+               u . inserted_at  <  ^ from  and  u . inserted_at  >=  ^ period_start  and  ( u . status  ==  :active  or  u . status  ==  :paid ) 
49+             ) , 
50+           success_previous: 
51+              u . id 
52+             |>  count ( ) 
53+             |>  filter ( 
54+               u . inserted_at  <  ^ period_start  and  u . inserted_at  >=  ^ previous_period_start  and 
55+                 ( u . status  ==  :active  or  u . status  ==  :paid ) 
56+             ) 
57+         } 
58+ 
59+     companies_query  = 
60+       from  u  in  User , 
61+         where:  u . inserted_at  >=  ^ period_start  and  u . type  ==  :organization , 
62+         right_join:  c  in  Contract , 
63+         on:  c . client_id  ==  u . id , 
64+         group_by:  u . id , 
65+         select:  % { 
66+           id:  u . id , 
67+           name:  u . name , 
68+           handle:  u . handle , 
69+           joined_at:  u . inserted_at , 
70+           total_contracts:  c . id  |>  count ( )  |>  filter ( c . inserted_at  >=  ^ period_start ) , 
71+           successful_contracts: 
72+              c . id  |>  count ( )  |>  filter ( c . status  ==  :active  or  ( c . status  ==  :paid  and  c . inserted_at  >=  ^ period_start ) ) , 
73+           last_active_at:  u . updated_at , 
74+           avatar_url:  u . avatar_url 
75+         } 
76+ 
77+     Ecto.Multi . new ( ) 
78+     |>  Ecto.Multi . one ( :orgs ,  orgs_query ) 
79+     |>  Ecto.Multi . one ( :contracts ,  contracts_query ) 
80+     |>  Ecto.Multi . all ( :companies ,  companies_query ) 
81+     |>  Repo . transaction ( ) 
82+     |>  case  do 
83+       { :ok ,  resp }  -> 
84+         % { 
85+           orgs:  orgs , 
86+           contracts:  contracts , 
87+           companies:  companies 
88+         }  =  resp 
89+ 
90+         current_success_rate  =  calculate_success_rate ( contracts . success_current ,  contracts . count_current ) 
91+         previous_success_rate  =  calculate_success_rate ( contracts . success_previous ,  contracts . count_previous ) 
92+ 
93+         { :ok , 
94+          % { 
95+            total_companies:  orgs . count_all , 
96+            companies_change:  orgs . count_current , 
97+            companies_trend:  calculate_trend ( orgs . count_current ,  orgs . count_previous ) , 
98+            active_companies:  orgs . active_all , 
99+            active_change:  orgs . active_current , 
100+            active_trend:  calculate_trend ( orgs . active_current ,  orgs . active_previous ) , 
101+            # TODO track time when contract is filled 
102+            avg_time_to_fill:  4.2 , 
103+            time_to_fill_change:  - 0.8 , 
104+            time_to_fill_trend:  :down , 
105+            contract_success_rate:  current_success_rate , 
106+            previous_contract_success_rate:  previous_success_rate , 
107+            success_rate_change:  current_success_rate  -  previous_success_rate , 
108+            success_rate_trend:  calculate_trend ( current_success_rate ,  previous_success_rate ) , 
109+            companies: 
110+               Enum . map ( companies ,  fn  company  -> 
111+                Map . merge ( company ,  % { 
112+                  success_rate:  calculate_success_rate ( company . successful_contracts ,  company . total_contracts ) , 
113+                  status:  if ( company . successful_contracts  >  0 ,  do:  :active ,  else:  :inactive ) 
114+                } ) 
115+              end ) 
116+          } } 
117+ 
118+       { :error ,  reason }  -> 
119+         { :error ,  reason } 
120+     end 
23121  end 
24122
25-   def  get_funnel_data ( _period  \\  "30d" )  do 
123+   def  get_funnel_data ( _period  \\  "30d" ,   _from   \\   DateTime . utc_now ( ) )  do 
26124    # Mock funnel data 
27125    % { 
28126      registered:  100 , 
@@ -34,19 +132,10 @@ defmodule Algora.Analytics do
34132    } 
35133  end 
36134
37-   defp  mock_companies  do 
38-     [ 
39-       % { 
40-         name:  "TechCorp" , 
41-         handle:  "techcorp" , 
42-         avatar_url:  "https://example.com/avatar1.jpg" , 
43-         joined_at:  ~U[ 2024-01-15 00:00:00Z]  , 
44-         status:  :active , 
45-         total_contracts:  12 , 
46-         success_rate:  95 , 
47-         last_active_at:  ~U[ 2024-03-18 14:30:00Z]  
48-       } 
49-       # Add more mock companies... 
50-     ] 
51-   end 
135+   defp  calculate_success_rate ( successful ,  total )  when  successful  ==  0  or  total  ==  0 ,  do:  0.0 
136+   defp  calculate_success_rate ( successful ,  total ) ,  do:  Float . ceil ( successful  /  total  *  100 ,  0 ) 
137+ 
138+   defp  calculate_trend ( a ,  b )  when  a  >  b ,  do:  :up 
139+   defp  calculate_trend ( a ,  b )  when  a  <  b ,  do:  :down 
140+   defp  calculate_trend ( a ,  b )  when  a  ==  b ,  do:  :same 
52141end 
0 commit comments