1
1
defmodule Algora.Analytics do
2
2
@ 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
4
12
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 )
6
15
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
23
121
end
24
122
25
- def get_funnel_data ( _period \\ "30d" ) do
123
+ def get_funnel_data ( _period \\ "30d" , _from \\ DateTime . utc_now ( ) ) do
26
124
# Mock funnel data
27
125
% {
28
126
registered: 100 ,
@@ -34,19 +132,10 @@ defmodule Algora.Analytics do
34
132
}
35
133
end
36
134
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
52
141
end
0 commit comments