1
+ from dash import Dash , html , dcc , callback , Output , Input , State , dash_table
2
+ import dash_bootstrap_components as dbc
3
+ import plotly .express as px
4
+ import pandas as pd
5
+ import datetime
6
+ import io
7
+ import os
8
+ import plotly .graph_objects as go
9
+ import base64
10
+
11
+ app = Dash (__name__ )
12
+
13
+ ROOT_DIR = os .path .dirname (os .path .dirname ((os .path .abspath (__file__ ))))
14
+ DATA_DIR = os .path .join (ROOT_DIR , 'data' )
15
+
16
+ countries_df = pd .read_csv (os .path .join (DATA_DIR , 'Countries.csv' ))
17
+ dates_df = pd .read_csv (os .path .join (DATA_DIR , 'Dates.csv' ))
18
+ devices_df = pd .read_csv (os .path .join (DATA_DIR , 'Devices.csv' ))
19
+ filter_df = pd .read_csv (os .path .join (DATA_DIR , 'Filters.csv' ))
20
+ pages_df = pd .read_csv (os .path .join (DATA_DIR , 'Pages.csv' ))
21
+ queries_df = pd .read_csv (os .path .join (DATA_DIR , 'Queries.csv' ))
22
+
23
+ dates_df ['Date' ] = pd .to_datetime (dates_df ['Date' ])
24
+ dates_df ['Day' ] = dates_df ['Date' ].dt .day_name ()
25
+
26
+
27
+
28
+ app .layout = html .Div ([
29
+ html .H1 ('Google Analytics Dashboard by CodePerfectPlus' ,
30
+ style = {'text-align' : 'center' , 'color' : 'black' , 'margin-top' : '2%' , 'margin-bottom' : '2%' }),
31
+
32
+ html .H2 ('Upload your data to see the dashboard' , style = {'text-align' : 'center' }),
33
+ html .Div ([
34
+ dcc .Upload (
35
+ id = 'upload-data' ,
36
+ children = html .Div ([
37
+ 'Drag and Drop or ' ,
38
+ html .A ('Select Files' )
39
+ ]),
40
+ style = {
41
+ 'width' : '100%' ,
42
+ 'height' : '60px' ,
43
+ 'lineHeight' : '60px' ,
44
+ 'borderWidth' : '1px' ,
45
+ 'borderStyle' : 'dashed' ,
46
+ 'borderRadius' : '5px' ,
47
+ 'textAlign' : 'center' ,
48
+ 'margin-bottom' : '2%'
49
+ },
50
+ # Allow multiple files to be uploaded
51
+ multiple = True
52
+ ),
53
+ ]),
54
+ dcc .Interval (
55
+ id = "load_interval" ,
56
+ n_intervals = 0 ,
57
+ max_intervals = 0 , #<-- only run once
58
+ interval = 1
59
+ ),
60
+ # create a line here
61
+ html .Hr (),
62
+
63
+ html .Div ([
64
+ # datepicker
65
+ html .Div ([
66
+ html .Div ([
67
+ dcc .DatePickerRange (
68
+ id = 'date-picker' ,
69
+ start_date = datetime .date (2024 , 1 , 1 ),
70
+ end_date = datetime .date (2024 , 12 , 31 ),
71
+ display_format = 'YYYY-MM-DD' ,
72
+ style = {'margin-right' : '10px' , 'height' : '60px' , 'width' : '40%' }
73
+ ),
74
+ dash_table .DataTable (id = 'total_clicks_impressions' ,
75
+ style_cell = {'textAlign' : 'center' },
76
+ style_header = {'backgroundColor' : 'lightblue' },
77
+ style_data = {'backgroundColor' : 'white' }),
78
+ # create both item side by side little space between them in center of the site beautifully
79
+ ], style = {'display' : 'flex' , 'justify-content' : 'center' })
80
+ ]),
81
+ # styble table in centre of the page and style with bootstrap color and width and margin from left and right 10%
82
+
83
+ dcc .Graph (id = 'overall-traffic' ),
84
+ dcc .Graph (id = 'day-wise-click' ),
85
+ # create below two graphs in one row
86
+ html .Div ([
87
+ dcc .Graph (id = 'device-wise-click' ),
88
+ dcc .Graph (id = 'country-wise-click' )
89
+ ], style = {'display' : 'flex' })
90
+ ]),
91
+ # create a line here
92
+ html .Hr (),
93
+ # Data without date filter
94
+ html .H2 ('Top Queries and Pages' , style = {'text-align' : 'center' }),
95
+ html .Hr (),
96
+ dash_table .DataTable (id = 'top-query' , style_cell = {'textAlign' : 'center' , 'margin-bottom' : '10px' },
97
+ style_header = {'backgroundColor' : 'lightblue' }, style_data = {'backgroundColor' : 'white' }),
98
+ dash_table .DataTable (id = 'top-page' , style_cell = {'textAlign' : 'center' },
99
+ style_header = {'backgroundColor' : 'lightblue' }, style_data = {'backgroundColor' : 'white' })
100
+
101
+ ])
102
+
103
+ @app .callback (
104
+ Output ('total_clicks_impressions' , 'data' ),
105
+ Output ('overall-traffic' , 'figure' ),
106
+ Output ('day-wise-click' , 'figure' ),
107
+ Output ('device-wise-click' , 'figure' ),
108
+ Output ('country-wise-click' , 'figure' ),
109
+ [Input ('date-picker' , 'start_date' ),
110
+ Input ('date-picker' , 'end_date' )]
111
+ )
112
+ def update_overall_traffic (start_date , end_date ):
113
+ start_date = pd .to_datetime (start_date )
114
+ end_date = pd .to_datetime (end_date )
115
+
116
+ filtered_df = dates_df [(dates_df ['Date' ] >= start_date ) & (dates_df ['Date' ] <= end_date )]
117
+ # click and impression data bs date
118
+ click_data = filtered_df .groupby ('Date' ).sum ()['Clicks' ]
119
+ impression_data = filtered_df .groupby ('Date' ).sum ()['Impressions' ]
120
+
121
+ total_clicks = click_data .sum ()
122
+ total_impressions = impression_data .sum ()
123
+
124
+ # total clicks and impressions table
125
+ data = [{'Total Clicks' : total_clicks , 'Total Impressions' : total_impressions }]
126
+
127
+ fig = go .Figure ()
128
+ # left y side clicks | right y side impressions
129
+ fig .add_trace (go .Scatter (x = click_data .index , y = click_data , mode = 'lines' , name = 'Clicks' ))
130
+ fig .add_trace (go .Scatter (x = impression_data .index , y = impression_data , mode = 'lines' , name = 'Impressions' , yaxis = 'y2' ))
131
+ fig .update_layout (
132
+ title = 'Clicks and Impressions Over Time' ,
133
+ xaxis_title = 'Date' ,
134
+ yaxis_title = 'Clicks (left side)' ,
135
+ yaxis2 = dict (
136
+ title = 'Impressions (right side)' ,
137
+ overlaying = 'y' ,
138
+ side = 'right'
139
+ )
140
+ )
141
+ # groupby on day and sum of clicks
142
+ filtered_df_fig_2 = filtered_df .copy (deep = True )
143
+ filtered_df_fig_2 .drop ('Date' , axis = 1 , inplace = True )
144
+ filtered_df_fig_2 = filtered_df_fig_2 .groupby ('Day' ).sum ().reset_index ()
145
+
146
+ fig2 = go .Figure ()
147
+ fig2 .add_trace (go .Bar (x = filtered_df_fig_2 ['Day' ], y = filtered_df_fig_2 ['Clicks' ]))
148
+ fig2 .update_layout (title = 'Day Wise Clicks' )
149
+
150
+ # Device wise clicks
151
+ device_clicks = devices_df .groupby ('Device' ).sum ()['Clicks' ]
152
+ fig3 = go .Figure ()
153
+
154
+ fig3 .add_trace (go .Pie (labels = device_clicks .index , values = device_clicks ))
155
+ fig3 .update_layout (title = 'Device Wise Clicks' )
156
+
157
+ # Country wise clicks
158
+ country_clicks = countries_df .groupby ('Country' ).sum ()['Clicks' ]
159
+ # check top 5 countries with highest clicks
160
+ country_clicks = country_clicks .sort_values (ascending = False ).head (5 )
161
+ fig4 = go .Figure ()
162
+ fig4 .add_trace (go .Pie (labels = country_clicks .index , values = country_clicks ))
163
+ fig4 .update_layout (title = 'Country Wise Clicks' )
164
+
165
+ # top 5 queries in table format
166
+ top_queries = queries_df .groupby ('Top queries' ).sum ().sort_values ('Clicks' , ascending = False ).head (5 )
167
+ top_queries = top_queries .reset_index ()
168
+ top_queries = top_queries .to_dict ('records' )
169
+
170
+
171
+ # bar chart for the top 5 pages
172
+ top_pages = pages_df .groupby ('Top pages' ).sum ().sort_values ('Clicks' , ascending = False ).head (5 )
173
+ top_pages = top_pages .reset_index ()
174
+ top_pages = top_pages .to_dict ('records' )
175
+
176
+
177
+
178
+ return data , fig , fig2 , fig3 , fig4
179
+
180
+ @app .callback (
181
+ Output ('upload-data' , 'children' ),
182
+ [Input ('upload-data' , 'filename' ),
183
+ Input ('upload-data' , 'contents' )]
184
+ )
185
+ def update_output (uploaded_filenames , uploaded_file_contents ):
186
+ if uploaded_filenames is not None and uploaded_file_contents is not None :
187
+ for name , data in zip (uploaded_filenames , uploaded_file_contents ):
188
+ if name .endswith ('.csv' ):
189
+ data = data .encode ('utf8' ).split (b';base64,' )[1 ]
190
+ with open (os .path .join (DATA_DIR , name ), 'wb' ) as file :
191
+ file .write (base64 .decodebytes (data ))
192
+ return [html .Div (['File uploaded successfully!' ])]
193
+
194
+
195
+ @app .callback (
196
+ Output ('top-query' , 'data' ),
197
+ Output ('top-page' , 'data' ),
198
+ Input ('load_interval' , 'n_intervals' )
199
+ )
200
+ def update_top_query_page (n ):
201
+ top_queries = queries_df .groupby ('Top queries' ).sum ().sort_values ('Clicks' , ascending = False ).head (5 )
202
+ top_queries = top_queries .reset_index ()
203
+ top_queries = top_queries .to_dict ('records' )
204
+
205
+ top_pages = pages_df .groupby ('Top pages' ).sum ().sort_values ('Clicks' , ascending = False ).head (5 )
206
+ top_pages = top_pages .reset_index ()
207
+ top_pages = top_pages .to_dict ('records' )
208
+
209
+ return top_queries , top_pages
0 commit comments