├── datasets
├── operation.db
├── data_warehouse.db
├── sources
│ ├── Sales Orders.xls
│ └── Product Details.xls
├── __pycache__
│ ├── elt.cpython-38.pyc
│ ├── extract_load.cpython-38.pyc
│ ├── initialize_datawh.cpython-38.pyc
│ └── operation_database.cpython-38.pyc
├── extract_load.py
├── initialize_datawh.py
├── elt.py
├── operation_database.py
└── DBmanager.py
├── assets
├── images
│ ├── cmd_1.PNG
│ ├── cmd_2.PNG
│ ├── cmd_3.PNG
│ ├── cmd_4.PNG
│ ├── logo.png
│ ├── DATA FLOW.png
│ ├── dashboard.PNG
│ ├── KPI_indicator.png
│ ├── dashboard_graph.png
│ ├── dashboard_show.png
│ ├── dashboard_graph1.png
│ ├── dashboard_graph2.png
│ └── Product details and Sale Order Details.png
└── index.css
├── LICENSE
├── README.md
└── app.py
/datasets/operation.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thongekchakrit/dashboard_dash_mock_sales/HEAD/datasets/operation.db
--------------------------------------------------------------------------------
/assets/images/cmd_1.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thongekchakrit/dashboard_dash_mock_sales/HEAD/assets/images/cmd_1.PNG
--------------------------------------------------------------------------------
/assets/images/cmd_2.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thongekchakrit/dashboard_dash_mock_sales/HEAD/assets/images/cmd_2.PNG
--------------------------------------------------------------------------------
/assets/images/cmd_3.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thongekchakrit/dashboard_dash_mock_sales/HEAD/assets/images/cmd_3.PNG
--------------------------------------------------------------------------------
/assets/images/cmd_4.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thongekchakrit/dashboard_dash_mock_sales/HEAD/assets/images/cmd_4.PNG
--------------------------------------------------------------------------------
/assets/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thongekchakrit/dashboard_dash_mock_sales/HEAD/assets/images/logo.png
--------------------------------------------------------------------------------
/datasets/data_warehouse.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thongekchakrit/dashboard_dash_mock_sales/HEAD/datasets/data_warehouse.db
--------------------------------------------------------------------------------
/assets/images/DATA FLOW.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thongekchakrit/dashboard_dash_mock_sales/HEAD/assets/images/DATA FLOW.png
--------------------------------------------------------------------------------
/assets/images/dashboard.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thongekchakrit/dashboard_dash_mock_sales/HEAD/assets/images/dashboard.PNG
--------------------------------------------------------------------------------
/assets/images/KPI_indicator.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thongekchakrit/dashboard_dash_mock_sales/HEAD/assets/images/KPI_indicator.png
--------------------------------------------------------------------------------
/assets/images/dashboard_graph.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thongekchakrit/dashboard_dash_mock_sales/HEAD/assets/images/dashboard_graph.png
--------------------------------------------------------------------------------
/assets/images/dashboard_show.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thongekchakrit/dashboard_dash_mock_sales/HEAD/assets/images/dashboard_show.png
--------------------------------------------------------------------------------
/datasets/sources/Sales Orders.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thongekchakrit/dashboard_dash_mock_sales/HEAD/datasets/sources/Sales Orders.xls
--------------------------------------------------------------------------------
/assets/images/dashboard_graph1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thongekchakrit/dashboard_dash_mock_sales/HEAD/assets/images/dashboard_graph1.png
--------------------------------------------------------------------------------
/assets/images/dashboard_graph2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thongekchakrit/dashboard_dash_mock_sales/HEAD/assets/images/dashboard_graph2.png
--------------------------------------------------------------------------------
/datasets/sources/Product Details.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thongekchakrit/dashboard_dash_mock_sales/HEAD/datasets/sources/Product Details.xls
--------------------------------------------------------------------------------
/datasets/__pycache__/elt.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thongekchakrit/dashboard_dash_mock_sales/HEAD/datasets/__pycache__/elt.cpython-38.pyc
--------------------------------------------------------------------------------
/datasets/__pycache__/extract_load.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thongekchakrit/dashboard_dash_mock_sales/HEAD/datasets/__pycache__/extract_load.cpython-38.pyc
--------------------------------------------------------------------------------
/datasets/__pycache__/initialize_datawh.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thongekchakrit/dashboard_dash_mock_sales/HEAD/datasets/__pycache__/initialize_datawh.cpython-38.pyc
--------------------------------------------------------------------------------
/assets/images/Product details and Sale Order Details.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thongekchakrit/dashboard_dash_mock_sales/HEAD/assets/images/Product details and Sale Order Details.png
--------------------------------------------------------------------------------
/datasets/__pycache__/operation_database.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thongekchakrit/dashboard_dash_mock_sales/HEAD/datasets/__pycache__/operation_database.cpython-38.pyc
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Chakrit Thong Ek
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/datasets/extract_load.py:
--------------------------------------------------------------------------------
1 | '''
2 | extract and load source files into operation database
3 | '''
4 | # Importing required libraries
5 | import pandas as pd
6 | import sqlite3
7 | from sqlite3 import Error
8 | import glob
9 | import os
10 |
11 | # ------------------------------------------------------------------------------
12 | def main(folder_of_files):
13 |
14 | conn = sqlite3.connect('operation.db')
15 |
16 | files = glob.glob(os.path.join(folder_of_files,"*.xls"))
17 |
18 | if files == []:
19 | raise Exception('\nNo datasets found in DATASETS folder!')
20 |
21 | for file in files:
22 |
23 | # read xls files
24 | xls_file = file
25 | xl = pd.ExcelFile(xls_file)
26 |
27 | # loop through sheets in xls file
28 | # populate database based on tablename
29 | for sheet in xl.sheet_names:
30 | # getting tablename
31 | table_name = sheet.replace(' ', '_').upper()
32 | # getting dataframes
33 | df_tmp = xl.parse(sheet)
34 | df_tmp.to_sql(table_name, conn, if_exists='append', index = False)
35 |
36 | conn.commit()
37 | conn.close()
38 |
39 | # ------------------------------------------------------------------------------
40 | if __name__ == '__main__':
41 |
42 | try:
43 | main(r'..\datasets\sources')
44 | except Error as e:
45 | print(str(e) + ': Primary keys exists!')
46 | pass
--------------------------------------------------------------------------------
/assets/index.css:
--------------------------------------------------------------------------------
1 | /* BASIC TYPOGRAPHY */
2 | /* from https://github.com/oxalorg/sakura */html {
3 | font-size: 62.5%;
4 | font-family: serif;
5 | }
6 | body {
7 | font-size: 1.8rem;
8 | line-height: 1.618;
9 | max-width: 95em;
10 | margin: auto;
11 | color: #41444b;
12 | background-color: #f3f3f3;
13 | padding: 13px;
14 | padding-top: 25px;
15 | padding: 13px;
16 | padding-top: 25px;
17 |
18 | }@media (max-width: 684px) {
19 | body {
20 | font-size: 1.53rem;
21 | }
22 | }@media (max-width: 382px) {
23 | body {
24 | font-size: 2.35rem;
25 | }
26 | }h1, h2, h3, h4, h5, h6 {
27 | line-height: 1.1;
28 | font-family: Verdana, Geneva, sans-serif;
29 | font-weight: 700;
30 | overflow-wrap: break-word;
31 | word-wrap: break-word;
32 | -ms-word-break: break-all;
33 | word-break: break-word;
34 | -ms-hyphens: auto;
35 | -moz-hyphens: auto;
36 | -webkit-hyphens: auto;
37 | hyphens: auto;
38 |
39 | }h1 {
40 | font-size: 2.35em;
41 | }h2 {
42 | font-size: 2em;
43 | }h3 {
44 | font-size: 1.75em;
45 | }h4 {
46 | font-size: 1.5em;
47 | }h5 {
48 | font-size: 1.25em;
49 | }h6 {
50 | font-size: 1em;
51 | }
52 |
53 | .dash-table-container .row {
54 | margin: 0;
55 | }
56 |
57 | .column {
58 | padding: 10px;
59 | }
60 |
61 | .alert{
62 | box-shadow : 5px 4px 2.5px #dddddd;
63 | text-align: center;
64 | text-justify: distribute;
65 | font-size: 1.8rem;
66 | height: 50px;
67 | }
68 |
69 |
--------------------------------------------------------------------------------
/datasets/initialize_datawh.py:
--------------------------------------------------------------------------------
1 | '''
2 | create denormalized table for data warehouse
3 | change the table according to business needs
4 | '''
5 | import sqlite3
6 | import pandas as pd
7 | from sqlite3 import Error
8 |
9 | # ------------------------------------------------------------------------------
10 | def create_connection(dw_file):
11 | '''
12 | create connection with data warehouse
13 | '''
14 | try:
15 | conn = sqlite3.connect(dw_file)
16 | return conn
17 |
18 | except Error as e:
19 | print(e)
20 |
21 | # ------------------------------------------------------------------------------
22 | def create_table_sql(conn, sql_query):
23 | '''
24 | create denormalized table from sql_query
25 | '''
26 | try:
27 | cursor = conn.cursor()
28 | cursor.execute(sql_query)
29 |
30 | except Error as e:
31 | print(e)
32 |
33 | # ------------------------------------------------------------------------------
34 | def main():
35 | '''
36 | create denormolized framework for data
37 | '''
38 | database = 'data_warehouse.db'
39 |
40 | data_warehouse = '''
41 |
42 | CREATE TABLE IF NOT EXISTS MASTER_FILE(
43 | Year INT NOT NULL CHECK(length(Year) = 4),
44 | Quarter INT NOT NULL CHECK(Quarter <= 4 AND Quarter >= 1),
45 | CustomerName CHAR(30) NOT NULL,
46 | City CHAR(30) NOT NULL,
47 | Country CHAR(30) NOT NULL,
48 | Sales DECIMAL(10,2) NOT NULL,
49 | Discount DECIMAL(10,2) NOT NULL,
50 | Cost DECIMAL(10,2) NOT NULL,
51 | Profit DECIMAL(15,2) NOT NULL CHECK(Profit > 0),
52 | ProfitType CHAR(6) NOT NULL,
53 | Quantity INT NOT NULL,
54 | Products CHAR(30) NOT NULL,
55 | ProductCategory CHAR(30) NOT NULL,
56 | EmployeeName CHAR(30) NOT NULL,
57 | Latitude FLOAT NOT NULL,
58 | Longitude FLOAT NOT NULL
59 | );
60 | '''
61 |
62 | # create a database connection
63 | conn = create_connection(database)
64 |
65 | if conn is not None:
66 |
67 | # create
68 | create_table_sql(conn, data_warehouse)
69 |
70 | conn.commit()
71 |
72 | # ------------------------------------------------------------------------------
73 | if __name__ == '__main__':
74 |
75 | try:
76 | main()
77 | except:
78 | print('Database exists!')
--------------------------------------------------------------------------------
/datasets/elt.py:
--------------------------------------------------------------------------------
1 | '''
2 | connect with operation database
3 | create a denormalized dataframe for dataware house
4 | '''
5 |
6 | import pandas as pd
7 | import sqlite3
8 | from sqlalchemy import create_engine
9 | from sqlite3 import Error
10 |
11 | # ------------------------------------------------------------------------------
12 | def create_connection(db_file):
13 | '''
14 | create connection with operation database
15 | '''
16 | try:
17 | conn = sqlite3.connect(db_file)
18 | return conn
19 |
20 | except Error as e:
21 | return print (e)
22 |
23 | # ------------------------------------------------------------------------------
24 | def create_cursor(conn, sql_query):
25 | '''
26 | hold the rows returned by sql query
27 | '''
28 | try:
29 | cursor = conn.cursor()
30 |
31 | except:
32 | print('\nPlease check database path!')
33 |
34 | try:
35 | return cursor.execute(sql_query)
36 |
37 | except UnboundLocalError as u:
38 | print(u)
39 |
40 | else:
41 | print('\nTable in database and query does not match!')
42 |
43 | # ------------------------------------------------------------------------------
44 | def get_datawarehouse_dataframe():
45 | '''
46 | create a dataframe for dataware house
47 | '''
48 |
49 | db_file = 'operation.db'
50 |
51 | order_details_query = '''
52 | SELECT
53 | strftime('%Y', OH.OrderDate) as Year,
54 | CASE
55 | WHEN strftime('%m', OH.OrderDate) BETWEEN '01' AND '03' THEN 1
56 | WHEN strftime('%m', OH.OrderDate) BETWEEN '04' AND '06' THEN 2
57 | WHEN strftime('%m', OH.OrderDate) BETWEEN '07' AND '09' THEN 3
58 | ELSE '4'
59 | END AS Quarter,
60 | ContactName as CustomerName,
61 | City,
62 | Country,
63 | round(OD.Sales, 2) as Sales,
64 | round(OD.Discount, 2) as Discount,
65 | round(OD.Costs, 2) as Cost,
66 | round(OD.Profit, 2) as Profit,
67 | Quantity,
68 | CASE
69 | WHEN OD.Profit < 75 THEN 'Small'
70 | WHEN OD.Profit >= 75 AND Profit < 300 THEN 'Medium'
71 | ELSE 'Large'
72 | END AS ProfitType,
73 | P.ProductName as Products,
74 | CAT.CategoryName as ProductCategory,
75 | EmployeeName,
76 | Latitude,
77 | Longitude
78 | FROM
79 | ORDER_DETAILS as OD
80 | INNER JOIN ORDER_HEADER as OH
81 | ON OH.OrderID = OD.OrderID
82 | INNER JOIN CUSTOMERS as C
83 | ON C.CustomerNumber = OH.CustomerID
84 | INNER JOIN PRODUCTS as P
85 | ON P.ProductID = OD.ProductID
86 | INNER JOIN CATEGORIES as CAT
87 | ON CAT.CategoryID = P.CategoryID
88 | INNER JOIN EMPLOYEES as EM
89 | ON EM.EmployeeID = OH.EmployeeID
90 | '''
91 |
92 | try:
93 | conn = create_connection(db_file)
94 | except:
95 | print('\nCannot create connection to database!')
96 |
97 | try:
98 | cursor = create_cursor(conn, order_details_query)
99 | except:
100 | print('\nTable cannot be found!')
101 |
102 | try:
103 | master_df = pd.DataFrame(cursor, columns=[i[0] for i in cursor.description])
104 | except:
105 | print('\nCannot convert query into dataframe!')
106 |
107 | return master_df
108 |
109 | # ------------------------------------------------------------------------------
110 | if __name__ == '__main__':
111 |
112 | get_datawarehouse_dataframe()
113 |
114 |
115 |
--------------------------------------------------------------------------------
/datasets/operation_database.py:
--------------------------------------------------------------------------------
1 | '''
2 | Setting up tables for operation database
3 | '''
4 | import sqlite3
5 | from sqlite3 import Error
6 |
7 | # ------------------------------------------------------------------------------
8 | def create_connection(db_file):
9 | '''
10 | create a database connection to sqlite database
11 | '''
12 | try:
13 | conn = sqlite3.connect(db_file)
14 | return conn
15 |
16 | except Error as e:
17 | print(e)
18 |
19 | return conn
20 |
21 | # ------------------------------------------------------------------------------
22 | def create_table(conn, create_table_sql):
23 | '''
24 | create a table from the create_table_sql statement
25 | '''
26 | try:
27 | cursor = conn.cursor()
28 | cursor.execute(create_table_sql)
29 |
30 | except Error as e:
31 | print(e)
32 |
33 | # ------------------------------------------------------------------------------
34 | def main():
35 | '''
36 | create all tables
37 | '''
38 | database = 'operation.db'
39 |
40 | products = '''
41 |
42 | CREATE TABLE IF NOT EXISTS PRODUCTS(
43 | ProductID INTEGER NOT NULL UNIQUE PRIMARY KEY,
44 | ProductName CHAR(50),
45 | CategoryID INTEGER NOT NULL,
46 | SupplierID INTEGER NOT NULL
47 | );
48 | '''
49 |
50 | categories = '''
51 |
52 | CREATE TABLE IF NOT EXISTS CATEGORIES(
53 | CategoryID INTEGER NOT NULL UNIQUE PRIMARY KEY,
54 | CategoryName CHAR(50),
55 | Description CHAR(50)
56 | );
57 | '''
58 |
59 | suppliers = '''
60 |
61 | CREATE TABLE IF NOT EXISTS SUPPLIERS(
62 | SupplierID INTEGER NOT NULL UNIQUE PRIMARY KEY,
63 | Supplier CHAR(50),
64 | SupplierContact CHAR(50),
65 | SupplierCountry CHAR(50)
66 | );
67 | '''
68 |
69 | order_header = '''
70 |
71 | CREATE TABLE IF NOT EXISTS ORDER_HEADER(
72 | OrderID INTEGER NOT NULL UNIQUE PRIMARY KEY,
73 | OrderDate DATETIME,
74 | CustomerID INTEGER,
75 | EmployeeID INTEGER,
76 | FOREIGN KEY(CustomerID) REFERENCES CUSTOMERS(CustomerNumber),
77 | FOREIGN KEY(EmployeeID) REFERENCES EMPLOYEES(EmployeeID)
78 | );
79 | '''
80 |
81 | employees = '''
82 |
83 | CREATE TABLE IF NOT EXISTS EMPLOYEES(
84 | EmployeeID INTEGER NOT NULL UNIQUE PRIMARY KEY,
85 | EmployeeName CHAR(30) NOT NULL
86 | );
87 | '''
88 |
89 | customers = '''
90 |
91 | CREATE TABLE IF NOT EXISTS CUSTOMERS(
92 | CustomerNumber INTEGER NOT NULL UNIQUE PRIMARY KEY,
93 | ContactName CHAR(30),
94 | City CHAR(25),
95 | Country CHAR(50),
96 | Fax CHAR(15),
97 | Phone CHAR(15),
98 | Latitude FLOAT,
99 | Longitude FLOAT,
100 | Region CHAR(15),
101 | EmployeeID INTEGER
102 | );
103 | '''
104 |
105 | order_details = '''
106 |
107 | CREATE TABLE IF NOT EXISTS ORDER_DETAILS(
108 | ProductID INTEGER NOT NULL,
109 | OrderID INTEGER NOT NULL,
110 | Quantity INTEGER,
111 | Sales DECIMAL (15, 2),
112 | Discount DECIMAL(8,2),
113 | Costs DECIMAL(8,2),
114 | Profit DECIMAL(8,2),
115 | FOREIGN KEY(ProductID) REFERENCES PRODUCTS(ProductID),
116 | FOREIGN KEY(OrderID) REFERENCES ORDER_HEADER(OrderID),
117 | PRIMARY KEY(ProductID, OrderID)
118 | );
119 | '''
120 |
121 | # create a database connection
122 | conn = create_connection(database)
123 |
124 | # create Tables
125 |
126 | if conn is not None:
127 |
128 | # create products table
129 | create_table(conn, products)
130 |
131 | # create categories table
132 | create_table(conn, categories)
133 |
134 | # create supplier table
135 | create_table(conn, suppliers)
136 |
137 | # create order_header table
138 | create_table(conn, order_header)
139 |
140 | # create employee table
141 | create_table(conn, employees)
142 |
143 | # create customers table
144 | create_table(conn, customers)
145 |
146 | # create order_details table
147 | create_table(conn, order_details)
148 |
149 |
150 | conn.commit()
151 |
152 | else:
153 | print('\nError cannot create database connection.')
154 |
155 | # ------------------------------------------------------------------------------
156 | if __name__ == '__main__':
157 | try:
158 | main()
159 | except:
160 | print('\nDatabase exists!')
161 |
162 |
--------------------------------------------------------------------------------
/datasets/DBmanager.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | '''
3 | if operation database does not exist,
4 | create operation database and populate database
5 | with data from xls files.
6 |
7 | next, create data warehouse
8 | create data warehouse tables
9 | extract transformed data from operation.db
10 | populate the data warehouse with extracted data
11 | '''
12 |
13 | import elt
14 | import extract_load
15 | import glob
16 | import initialize_datawh
17 | import operation_database
18 | import os
19 | import sqlite3
20 | from sqlite3 import Error
21 |
22 | # ------------------------------------------------------------------------------
23 | # locating operation database
24 | file_path = r'../datasets/sources'
25 |
26 | def create_operation_database(file_path):
27 | '''
28 | create operation database
29 | populate operation database
30 | '''
31 | # setting up tables for operation database
32 | operation_database.main()
33 |
34 | try:
35 | # extract and load source files into operation database
36 | extract_load.main(file_path)
37 | print('\nDatabase created and table populated!')
38 |
39 | except Error as e:
40 | print(str(e) + ': Primary key exists!')
41 | pass
42 |
43 | # ------------------------------------------------------------------------------
44 | def get_master_data_file():
45 | '''
46 | create connection with operation database
47 | create a denormalized dataframe for dataware house
48 | '''
49 |
50 | try:
51 | return elt.get_datawarehouse_dataframe()
52 |
53 | except Error as e:
54 | print(e)
55 |
56 | # ------------------------------------------------------------------------------
57 | def check_operation_exists():
58 | '''
59 | check if operation.db exists in database_management folder
60 | '''
61 |
62 | # pointing to database_management folder
63 | folder_of_files = r'..\datasets'
64 |
65 | # search if 'operation.db' exists in folder
66 | files = glob.glob(os.path.join(folder_of_files, 'operation.db'))
67 |
68 | # return true if exists
69 | return True if files != [] else False
70 |
71 | # ------------------------------------------------------------------------------
72 | def check_datawh_exists():
73 | '''
74 | check if data_warehouse.db exists
75 | '''
76 |
77 | #pointing to dashboard folder
78 | folder_of_files = r'..\datasets'
79 |
80 | #search if data warehouse exists
81 | files = glob.glob(os.path.join(folder_of_files, 'data_warehouse.db'))
82 |
83 | # return true if exists
84 | return True if files != [] else False
85 |
86 | # ------------------------------------------------------------------------------
87 | def main():
88 |
89 | checker_operation = check_operation_exists()
90 | checker_warehouse = check_datawh_exists()
91 |
92 | if checker_operation != True:
93 |
94 | print('\nCreating operation database!')
95 | # set up operation database
96 | create_operation_database(file_path)
97 | # get master df
98 | # used for dataware house insertion
99 | master_df = get_master_data_file()
100 | print('\nGenerating dataframe for data warehouse!')
101 |
102 | else:
103 | print('\nOperation database exists, skipping to dataframe generation!')
104 | # perform dataframe extraction for data warehouse
105 | # if operation.db is detected
106 | master_df = get_master_data_file()
107 | print('\nDataframe for data warehouse generated successfully')
108 |
109 | if checker_warehouse != True:
110 |
111 | # set up data warehouse frame
112 | initialize_datawh.main()
113 | # insert master_df into data_warehouse
114 | try:
115 | conn = sqlite3.connect('data_warehouse.db')
116 | master_df.to_sql('MASTER_FILE' ,conn, if_exists='append', index = False)
117 | print('\nData warehouse has been populated!')
118 | conn.commit()
119 |
120 | except Error as e:
121 | print(e)
122 |
123 | else:
124 | # PROBLEM STATMENT
125 | # THE DATA WAREHOUSE FORMAT GETS RESET
126 | # FIND A WORK AROUND WAY FOR THIS
127 | try:
128 | # insert master_df into data_warehouse
129 | conn = sqlite3.connect('data_warehouse.db')
130 | master_df.to_sql('MASTER_FILE' ,conn, if_exists='append', index = False)
131 | print('\nData warehouse has been updated!')
132 | conn.commit()
133 |
134 | except Error as e:
135 | print(e)
136 |
137 | # ------------------------------------------------------------------------------
138 | if __name__ == '__main__':
139 |
140 | # run tasks
141 | main()
142 |
143 |
144 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Building Web Application Dashboard Using Python (Dash)
2 |
3 |
4 |
5 | The business intelligence dashboard is an important analytics tool that is used to visualize data across all industries. Using these dashboards, useful insights to Business Performance Management such as the critical reporting and metric information can be readily displayed and used by various departments to drive actionable business decisions.
6 |
7 | To help drive and generate better business decisions, the analytics team in Mock Company had developed a web application using Python programming language with the help of the Dash module. Our application aimed to aid the sales department in providing historic sales and profitability performance, and visualization of various aspects such as geographical sales performance monitoring and sales against profit by product category.
8 |
9 | This documentation highlights the prerequisites, data pipeline, and the demonstration of the use of the application.
10 |
11 | ## 1. Getting Started
12 | The instructions below will help you set up the environment.
13 |
14 | Prerequisites
15 | To use the program, ensure that [Python v3](https://www.python.org/downloads/) and the following libraries are installed on your operating system:
16 |
17 | ```
18 | 1. Dash
19 | 2. Plotly
20 | 3. Dash-bootstrap-components
21 | 4. sqlalchemy
22 | 5. Pandas
23 | ```
24 |
25 | If you do not have the modules installed on your system, please follow the instruction below.
26 |
27 |
28 | Installation on Windows using terminal:
29 | ```
30 | py -m pip install dash
31 | py -m pip install plotly
32 | py -m pip install dash-bootstrap-components
33 | py -m pip install sqlalchemy
34 | py -m pip install pandas
35 | ```
36 |
37 | Installation on Linux using terminal:
38 |
39 | ```
40 | $ pip install dash
41 | $ pip install plotly
42 | $ pip install dash-bootstrap-components
43 | $ pip install sqlalchemy
44 | $ pip install pandas
45 | ```
46 |
47 | ## 2. Data Pipeline
48 | Below is an illustration of the data flow for our application.
49 |
50 | Firstly, the data was extracted, loaded and normalized before insertion into a database via SQlite.
51 |
52 | Next, we then query the data from the database, perform ELT techniques, denormalized the data and transfer the data to a data warehouse.
53 |
54 | Lastly, we query the denormalized data from the data warehouse, run it through our application, and our application will visualize the data onto a web address.
55 |
56 |
57 |
58 | ## 3. Normalized Database Schema
59 |
60 |
61 |
62 | ## 4. Folder Structure
63 | The structure of the Dash application is presented below:
64 |
65 | ```
66 | - app.py
67 | - assets
68 | |-- index.css
69 | -images
70 | |-- logo.png
71 | - datasets
72 | |-- DBmanager.py
73 | |-- elt.py
74 | |-- extract_load.py
75 | |-- initialize_datawh.py
76 | |-- operation_database.py
77 | |-- data_warehouse.db (Generated with DBmanager.py)
78 | |-- operation.db (Generated with DBmanager.py)
79 | -sources
80 | |-- Product Details.xls
81 | |-- Sales Orders.xls
82 | ```
83 |
84 | ## 5. Demonstration
85 | The processes of running this application will be shown in this section.
86 |
87 | To run the program, firstly, we need to setup and populate both the database and data warehouse. This process will be performed using DBmanager.py and it can be performed as below.
88 |
89 | ```
90 | Type in cmd:
91 | cd Desktop
92 | cd Dashboard_sales_orders_product
93 | DBmanager.py
94 | ```
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 | After the population of database and data warehouse, the next step is to run the Dash application using app.py file. The Dash application will start, and as we set the app.py to run in development mode, a development IP server will be generated.
105 |
106 | ```
107 | Type in cmd:
108 | app.py
109 | ```
110 |
111 |
112 |
113 | Copy 'http://127.0.0.1:8050/' and insert it into any browser to show our dashboard.
114 |
115 | Capture.PNG
116 |
117 | In total, there are 4 visualization and 6 KPI shown.
118 |
119 |
120 |
121 |
122 |
123 | The data points on the data visualization can be hovered.
124 |
125 |
126 |
127 | Using dash callback python declarators, the first data figure can be interacted with. I've made a drop down menu, which parses in the variable to the plotly graph, and the graph will be generating depending on the variable.
128 |
129 |
130 |
131 |
132 |
133 | ## 6. Future implementation
134 | The future implementation of Conversational Analytics may be done using dash_core_components 'input' syntax.
135 |
136 | https://dash.plotly.com/dash-core-components/input
137 |
138 | ## Built With
139 | - Dash - Create Dashboard Web application, interact with HTML and CSS syntax
140 | - Sqlite3 - Simulate Data warehouse
141 | - Plotly - Use to visualize data
142 |
143 | ## Author
144 | [Chakrit Thong Ek](https://github.com/thongekchakrit)
--------------------------------------------------------------------------------
/app.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import dash
4 | import dash_bootstrap_components as dbc
5 | import dash_core_components as dcc
6 | import dash_html_components as html
7 | import dash_table
8 | from dash.dependencies import Input, Output
9 | import plotly.express as px
10 | import plotly.graph_objects as go
11 |
12 | import pandas as pd
13 | from sqlite3 import Error
14 | import sqlite3
15 | global master_df
16 |
17 | # ################################################################################
18 | '''
19 | create connection with data warehouse
20 | extract information of interest from data warehouse
21 | '''
22 |
23 | def create_connection():
24 |
25 | try:
26 | # create connection with data warehouse
27 | conn = sqlite3.connect(r'.\datasets\data_warehouse.db')
28 | return conn
29 |
30 | except Error as e:
31 | print(e)
32 |
33 | def fetch_master_df(conn):
34 | try:
35 | # create cursor to get data
36 | cursor = conn.cursor()
37 |
38 | # insert query into cursor
39 | cursor.execute('''
40 | SELECT * FROM MASTER_FILE
41 | ''')
42 |
43 | # convert query into dataframe
44 | return pd.DataFrame(cursor.fetchall(), columns=[i[0] for i in cursor.description])
45 |
46 | except Error as e:
47 | print(e)
48 |
49 | # ################################################################################
50 | # master file for satisfy visualization requirements
51 |
52 | # create connection
53 | conn = create_connection()
54 |
55 | # get full data frame
56 | master_df = fetch_master_df(conn)
57 |
58 | # ------------------------------------------------------------------------------
59 | # KPI and measures
60 | def get_revenue(master_df):
61 |
62 | try:
63 | return '$ {0:,.2f}K'.format((master_df['Sales'].sum()/1000))
64 |
65 | except Error as e:
66 | print(e)
67 | pass
68 |
69 | def get_expenses(master_df):
70 |
71 | try:
72 | return '$ {0:,.2f}K'.format((master_df['Cost'].sum()/1000))
73 |
74 | except Error as e:
75 | print(e)
76 | pass
77 |
78 | def get_sales_quantity(master_df):
79 | try:
80 | return '{0:} Units'.format((master_df['Quantity'].sum()))
81 |
82 | except Error as e:
83 | print(e)
84 | pass
85 |
86 | def get_discount(master_df):
87 | try:
88 | return '$ {0:,.2f}K'.format((master_df['Discount'].sum()/1000))
89 |
90 | except Error as e:
91 | print(e)
92 | pass
93 |
94 | def get_profit(master_df):
95 | try:
96 | return '$ {0:,.2f}K'.format((master_df['Profit'].sum()/1000))
97 |
98 | except Error as e:
99 | print(e)
100 | pass
101 |
102 | def get_sales_margin(master_df):
103 | try:
104 | return '{:,.2f}%'.format(((master_df['Profit'].sum())/(master_df['Sales'].sum()))*100)
105 |
106 | except Error as e:
107 | print(e)
108 | pass
109 |
110 | # ------------------------------------------------------------------------------
111 | # Scatter plot of Sales vs Profit by Category
112 | requirement_iii = master_df[['Sales', 'Discount', 'Cost', 'Profit', 'ProductCategory']].groupby('ProductCategory').sum().reset_index()
113 |
114 | viz_scatter_iii = px.scatter(requirement_iii, x="Sales", y="Profit", size="Profit", color="ProductCategory",
115 | hover_name="Profit", log_x=True, log_y=True, size_max=50, template="seaborn", title='SALES AGAINST PROFIT BY PRODUCT CATEGORY')
116 |
117 | layout = viz_scatter_iii.update_layout(
118 | hoverlabel=dict(
119 | bgcolor="white",
120 | font_size=16,
121 | font_family="Open Sans"
122 | ),
123 | height=700,
124 | width=700,
125 | title={
126 | 'y':0.9,
127 | 'x':0.5,
128 | 'xanchor': 'center',
129 | 'yanchor': 'top'},
130 | font=dict(
131 | size=16,
132 | color="#4a4a4a",
133 | ),paper_bgcolor="#f8f9fa")
134 |
135 | viz_scatter_iii.update_traces(mode="markers", hovertemplate='Sales: %{x}
Profit: %{y}')
136 |
137 | # ------------------------------------------------------------------------------
138 | # Sales by City
139 | # get sales from master file, aggregate on country
140 | city_sales = master_df[['Sales','Profit','Cost','Quantity','Discount','City']].groupby('City').sum()
141 | lat_log_city = master_df[['City', 'Latitude', 'Longitude']].drop_duplicates().set_index('City')
142 | requirement_ii = pd.concat([city_sales, lat_log_city], axis=1, join='inner').reset_index()
143 | requirement_ii['Field'] = ' City' +'
' + requirement_ii['City'] + '
Sales ' + '$' + (requirement_ii['Sales'].round(2).astype(str))
144 |
145 | mapbox_access_token = 'pk.eyJ1IjoiY2hha3JpdHRob25nZWsiLCJhIjoiY2tkdTAzd2hwMDBkZzJycWluMnFicXFhdCJ9.JjJhMoek5126u1B_kwYNiA'
146 |
147 | px.set_mapbox_access_token(mapbox_access_token)
148 |
149 | map_data = px.scatter_mapbox(requirement_ii, lat="Latitude", lon="Longitude", color="Sales", size="Sales",
150 | color_continuous_scale=px.colors.cyclical.Edge, size_max=20, zoom=1,
151 | center=dict(lon=-40.200033, lat=32.249974),
152 | hover_data={'City':False, 'Latitude':False, 'Longitude':False, 'Sales':False, 'Field':True}, title='SALES BY CITY', template="seaborn")
153 |
154 | map_data.update_layout(
155 | hoverlabel=dict(
156 | bgcolor="white",
157 | font_size=16,
158 | font_family="Open Sans"
159 | ),
160 | height=600,
161 | width=700,
162 | title={
163 | 'y':0.9,
164 | 'x':0.5,
165 | 'xanchor': 'center',
166 | 'yanchor': 'top'},
167 | font=dict(
168 | size=16,
169 | color="#4a4a4a"),
170 | paper_bgcolor="#f8f9fa"
171 | )
172 |
173 | # --------------------------------------------------------------------------------
174 | # Dash Table
175 | data_table = master_df[['Year', 'Quarter', 'Country', 'City', 'CustomerName', 'ProfitType', 'ProductCategory', 'Sales', 'Profit', 'Cost']]
176 |
177 | # ################################################################################
178 | # initilize dash
179 | # Bootstrap Javascript.
180 |
181 | BS = "https://stackpath.bootstrapcdn.com/bootswatch/4.5.2/litera/bootstrap.min.css"
182 |
183 | app = dash.Dash(__name__, external_stylesheets=[BS])
184 | server = app.server
185 |
186 | app.title = 'Mock Company Dashboard'
187 | # Define app layout
188 |
189 | app.layout = html.Div(
190 | html.Div([
191 | html.Div(
192 | [
193 | html.H1(
194 | 'Sales Department Data Analysis',
195 | style={
196 | 'fontSize':40,
197 | 'textAlign':'center',
198 | 'textDirection': 'vertical',
199 | 'dir':'rtl',
200 | 'padding': '20px',
201 | 'padding-top': '70px',
202 | 'color': '#41444b',
203 | 'margin-left':'auto',
204 | 'margin-right':'auto'
205 | },
206 | className='eight columns'),
207 |
208 | html.Img(
209 | src="/assets/images/logo.png",
210 | className='four columns',
211 | style={
212 | 'height': '7%',
213 | 'width': '7%',
214 | 'float': 'left',
215 | 'position': 'relative',
216 | 'margin-top': 10,
217 | 'align':'center'
218 | },
219 | ),
220 | ], className="row"
221 | ),
222 |
223 | dbc.Row([
224 |
225 | dbc.Col(html.Div(
226 | dbc.Alert("QUANTITY " +
227 | get_sales_quantity(master_df), color='light')), width=2),
228 |
229 | dbc.Col(html.Div(
230 | dbc.Alert("REVENUE " +
231 | get_revenue(master_df), color='light',)), width=2),
232 |
233 | dbc.Col(html.Div(
234 | dbc.Alert("EXPENSES " +
235 | get_expenses(master_df), color='light')), width=2),
236 |
237 | dbc.Col(html.Div(
238 | dbc.Alert("DISCOUNT " +
239 | get_discount(master_df), color='light')), width=2),
240 |
241 | dbc.Col(html.Div(
242 | dbc.Alert("PROFIT " +
243 | get_profit(master_df), color='light')), width=2),
244 |
245 | dbc.Col(html.Div(
246 | dbc.Alert("MARGIN " +
247 | get_sales_margin(master_df), color='light')), width=2),
248 |
249 | ], align="center",
250 | justify="center"),
251 |
252 | html.Div(
253 | [
254 | html.Div([
255 | dcc.Dropdown(
256 | id = 'Cities',
257 | options=[
258 | {'label': 'Country', 'value': 'Country'},
259 | {'label': 'Employee', 'value': 'EmployeeName'}
260 | ],
261 | value='Country',
262 | style={'height': 'auto', 'width': '700px', 'align':'center'}
263 | ),
264 | dcc.Graph(
265 | id='bar_graph'
266 | )
267 | ], className= 'six columns',
268 | style={'border-radius': '15px',
269 | 'backgroundColor': '#f8f9fa',
270 | 'box-shadow' : '4px 4px 2.5px #dddddd',
271 | 'padding':'20px', 'margin-left':'auto','margin-right':'auto', 'margin-top':'25px'}
272 | ),
273 |
274 | html.Div([
275 | dcc.Graph(
276 | id='graph-4',
277 | figure=map_data
278 | )
279 | ], className= 'six columns',
280 | style={'border-radius': '15px',
281 | 'backgroundColor': '#f8f9fa',
282 | 'box-shadow' : '4px 4px 2.5px #dddddd',
283 | 'padding':'20px', 'margin-left':'auto','margin-right':'auto', 'margin-top':'25px'}
284 | )
285 | ], className="row"
286 | ),
287 |
288 |
289 | html.Div(
290 | [
291 | html.Div([
292 | dash_table.DataTable(
293 | id='datatable-interactivity',
294 | data=data_table.to_dict('records'),
295 | columns=[{'name': i, 'id': i, "deletable":True,"selectable":True} for i in data_table.columns],
296 | editable = True,
297 | sort_action="native",
298 | sort_mode="multi",
299 | column_selectable="single",
300 | row_selectable="multi",
301 | row_deletable=True,
302 | selected_columns=[],
303 | selected_rows=[],
304 | page_action="native",
305 | page_current= 0,
306 | page_size=55,
307 | style_cell={
308 | 'textOverflow': 'ellipsis',
309 | 'overflow': 'hidden'
310 | },
311 | style_table={
312 | 'width':'700px',
313 | 'height':'600px',
314 | 'overflowY':'auto',
315 | 'overflowX': 'auto',
316 | 'align': 'center'
317 | },
318 | style_header={
319 | 'backgroundColor': 'rgb(230, 230, 230)',
320 | 'fontWeight': 'bold'
321 | }
322 | )
323 | ], className= 'six columns',
324 | style={'border-radius': '15px',
325 | 'backgroundColor': '#f8f9fa',
326 | 'box-shadow' : '4px 4px 2.5px #dddddd',
327 | 'padding':'20px', 'margin-left':'auto','margin-right':'auto', 'margin-top':'25px'}),
328 | html.Div([
329 | dcc.Graph(
330 | id='graph-1', figure=viz_scatter_iii
331 | )
332 | ], className= 'six columns',
333 | style={'border-radius': '15px',
334 | 'backgroundColor': '#f8f9fa',
335 | 'box-shadow' : '4px 4px 2.5px #dddddd',
336 | 'padding':'20px', 'margin-left':'auto','margin-right':'auto', 'margin-top':'25px'}
337 | ),
338 | ], className="row"
339 | )
340 | ], className='ten columns offset-by-one')
341 | )
342 |
343 | # ################################################################################
344 | # Connect the Plotly graphs with Dash Components
345 |
346 | # Interactivity with table
347 |
348 | @app.callback(
349 | dash.dependencies.Output('bar_graph', 'figure'),
350 | [dash.dependencies.Input('Cities', 'value')])
351 | def select_cat(selector):
352 | revenue_country = master_df[['Country','Sales', 'EmployeeName']]
353 | revenue_country = revenue_country.groupby(selector).sum().reset_index().sort_values('Sales')
354 |
355 | revenue_country_viz = px.bar(revenue_country, y=selector, x='Sales', text='Sales', template="seaborn",
356 | hover_data={'Sales':':.2f'}, title='REVENUE BY ' + str(selector).upper())
357 |
358 | revenue_country_viz.update_traces(texttemplate='%{text:.2s}', textposition='outside')
359 | revenue_country_viz.update_layout(uniformtext_minsize=7, uniformtext_mode='hide', height=600)
360 |
361 | revenue_country_viz.update_layout(
362 | hoverlabel=dict(
363 | bgcolor="white",
364 | font_size=16,
365 | font_family="Open Sans"
366 | ),
367 | title={
368 | 'y':0.9,
369 | 'x':0.5,
370 | 'xanchor': 'center',
371 | 'yanchor': 'top'},
372 | font=dict(
373 | size=16,
374 | color="#4a4a4a"
375 | ),
376 | paper_bgcolor="#f8f9fa")
377 | return revenue_country_viz
378 | # def update_table(input_value):
379 | # return generate_table(df_table.sort_values([input_value], ascending=[False]).reset_index())
380 |
381 | # @app.callback(
382 | # Output(component_id='bar-chart', component_property='figure'),
383 | # [Input(component_id='bubble-chart', component_property='hoverData')]
384 | # )
385 |
386 | # def update_graph(master_df):
387 | # return bar(master_df)
388 |
389 |
390 | # ################################################################################
391 | if __name__ == '__main__':
392 |
393 | app.run_server(debug=True)
394 |
--------------------------------------------------------------------------------