Skip to content

Commit dcca8e8

Browse files
intial version of dashboard
1 parent 79b320c commit dcca8e8

File tree

5 files changed

+227
-2
lines changed

5 files changed

+227
-2
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,4 @@ cython_debug/
158158
# and can be added to the global gitignore or merged into this file. For a more nuclear
159159
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
160160
#.idea/
161+
data/

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,8 @@ website performance dashboard on google search console data
33

44

55
# Export Anconda Yaml File
6-
conda env export | grep -v "^prefix: " > environment.yml
6+
conda env export | grep -v "^prefix: " > environment.yml
7+
8+
# Features
9+
10+
1. Support Reupload the Data.

app.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from src.main import app
2+
3+
4+
if __name__ == '__main__':
5+
app.run_server(debug=True, port=8080, host='0.0.0.0')

environment.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ dependencies:
2020
- setuptools=68.2.2=py311h06a4308_0
2121
- sqlite=3.41.2=h5eee18b_0
2222
- tk=8.6.12=h1ccaba5_0
23-
- tzdata=2023d=h04d1e81_0
2423
- wheel=0.41.2=py311h06a4308_0
2524
- xz=5.4.5=h5eee18b_0
2625
- zlib=1.2.13=h5eee18b_0
@@ -33,20 +32,27 @@ dependencies:
3332
- dash-core-components==2.0.0
3433
- dash-html-components==2.0.0
3534
- dash-table==5.0.0
35+
- et-xmlfile==1.1.0
3636
- flask==3.0.2
3737
- idna==3.6
3838
- importlib-metadata==7.0.1
3939
- itsdangerous==2.1.2
4040
- jinja2==3.1.3
4141
- markupsafe==2.1.5
4242
- nest-asyncio==1.6.0
43+
- numpy==1.26.4
44+
- openpyxl==3.1.2
4345
- packaging==23.2
46+
- pandas==2.2.0
4447
- plotly==5.19.0
48+
- python-dateutil==2.8.2
49+
- pytz==2024.1
4550
- requests==2.31.0
4651
- retrying==1.3.4
4752
- six==1.16.0
4853
- tenacity==8.2.3
4954
- typing-extensions==4.9.0
55+
- tzdata==2024.1
5056
- urllib3==2.2.0
5157
- werkzeug==3.0.1
5258
- zipp==3.17.0

src/main.py

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
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

Comments
 (0)