Shafeek Saleem commited on
Commit
de719e5
β€’
1 Parent(s): 3657410
.idea/.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ # Default ignored files
2
+ /shelf/
3
+ /workspace.xml
.idea/aieye_tutorial_template.iml ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="PYTHON_MODULE" version="4">
3
+ <component name="NewModuleRootManager">
4
+ <content url="file://$MODULE_DIR$" />
5
+ <orderEntry type="inheritedJdk" />
6
+ <orderEntry type="sourceFolder" forTests="false" />
7
+ </component>
8
+ </module>
.idea/inspectionProfiles/Project_Default.xml ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <component name="InspectionProjectProfileManager">
2
+ <profile version="1.0">
3
+ <option name="myName" value="Project Default" />
4
+ <inspection_tool class="PyPep8Inspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
5
+ <option name="ignoredErrors">
6
+ <list>
7
+ <option value="E402" />
8
+ </list>
9
+ </option>
10
+ </inspection_tool>
11
+ <inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
12
+ <option name="ignoredErrors">
13
+ <list>
14
+ <option value="N806" />
15
+ </list>
16
+ </option>
17
+ </inspection_tool>
18
+ <inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
19
+ <option name="ignoredIdentifiers">
20
+ <list>
21
+ <option value="trx_clus" />
22
+ <option value="matrix_demand_transfer" />
23
+ </list>
24
+ </option>
25
+ </inspection_tool>
26
+ </profile>
27
+ </component>
.idea/inspectionProfiles/profiles_settings.xml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ <component name="InspectionProjectProfileManager">
2
+ <settings>
3
+ <option name="USE_PROJECT_PROFILE" value="false" />
4
+ <version value="1.0" />
5
+ </settings>
6
+ </component>
.idea/modules.xml ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/.idea/aieye_tutorial_template.iml" filepath="$PROJECT_DIR$/.idea/aieye_tutorial_template.iml" />
6
+ </modules>
7
+ </component>
8
+ </project>
.idea/sonarlint/issuestore/index.pb ADDED
File without changes
.idea/vcs.xml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="$PROJECT_DIR$" vcs="Git" />
5
+ </component>
6
+ </project>
0_Introduction.py CHANGED
@@ -8,17 +8,25 @@ initialize_level()
8
  LEVEL=0
9
 
10
  def intro_page():
11
- st.header("AI Eye Tutorial Template")
12
- st.subheader("Level 0: Introduction")
13
 
14
- st.write("""Welcome to the AI Eye Tutorial Template! This template is designed to help you create your own AI Eye tutorial.
15
- The template is divided into levels, and each level has a set of tasks that you need to complete before you can move on to the next level.
16
- You can use this template to create your own tutorial by completing the tasks in each level and adding your own content. You can also use
17
- this template to learn how to use AI Eye by completing the tasks in each level.""")
18
 
19
- st.info(f"Current Level: {get_level()}")
 
 
 
20
 
21
- if st.button("Complete"):
 
 
 
 
 
 
 
 
22
  complete_level(LEVEL)
23
 
24
  render_page(intro_page, LEVEL)
 
8
  LEVEL=0
9
 
10
  def intro_page():
11
+ st.header("Weather Forecasting")
12
+ st.subheader("Introduction")
13
 
14
+ st.write("""Welcome to the interactive tutorial on creating your very own Weather Forecasting Application!""")
 
 
 
15
 
16
+ st.image(
17
+ "https://www.beckwithweather.com/wp-content/uploads/sites/1134/2020/06/860_main_weather_and_prediction.png",
18
+ use_column_width=True,
19
+ )
20
 
21
+ st.write(
22
+ """
23
+ In this tutorial, you will learn how to build a simple application that can forecast weather conditions to make informed decisions. Weather forecasting plays a pivotal role in our daily lives, influencing our travel plans, agricultural activities, energy consumption, and overall decision-making.
24
+ """
25
+ )
26
+
27
+ st.info(f"Click on the button below to start the tutorial!")
28
+
29
+ if st.button("I am Ready!"):
30
  complete_level(LEVEL)
31
 
32
  render_page(intro_page, LEVEL)
datasets/Building_forcasting.csv ADDED
The diff for this file is too large to render. See raw diff
 
pages/1_Step_1.py DELETED
@@ -1,15 +0,0 @@
1
- import streamlit as st
2
- from utils.levels import complete_level, render_page, initialize_level
3
- LEVEL = 1
4
-
5
- initialize_level()
6
-
7
- def step1_page():
8
- st.header("Step 1")
9
- st.subheader("Level 1: Introduction")
10
- st.write("This is the first step of the tutorial. You can add your own content here.")
11
-
12
- if st.button("Complete"):
13
- complete_level(LEVEL)
14
-
15
- render_page(step1_page, LEVEL)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pages/1_Weather Forecasting and It's Applications.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from utils.levels import complete_level, render_page, initialize_level
3
+ LEVEL = 1
4
+
5
+ initialize_level()
6
+
7
+ def step1_page():
8
+ st.header("Weather Forecasting and It's Applications")
9
+ st.markdown(
10
+ """
11
+ ### What is weather forecasting?
12
+ Weather forecasting is an application of science and advanced technology that is used to predict the atmospheric condition
13
+ for an upcoming point of time and a given location.
14
+
15
+ Our day to day life is directly or indirectly depends on the weather in terms of economy and environment,
16
+ it affects us with various factors as events, timing, duration, location, etc. In consideration of these factors,
17
+ weather forecasting works with the parameters temperature, humidity, and wind speed.
18
+
19
+ ### Applications of weather forecasting
20
+ Weather forecasting has numerous real-world applications across various industries and sectors.
21
+ Its impact extends beyond personal planning and extends to critical decision-making processes in sectors that heavily rely on weather conditions.
22
+ Here are some key applications of weather forecasting in the real world:
23
+
24
+ 1. **Aviation Industry**: Weather forecasting plays a vital role in aviation, enabling pilots and airlines to plan safe flight routes, avoid turbulence, and make decisions regarding takeoff, landing, and flight diversions based on current and predicted weather conditions.
25
+
26
+ 2. **Agriculture and Farming**: Farmers use weather forecasts to optimize irrigation schedules, plan planting and harvesting activities, and protect crops from adverse weather conditions, such as frost, drought, or excessive rainfall.
27
+
28
+ 3. **Energy and Utilities**: Weather forecasts are crucial for energy and utility companies to anticipate electricity demand, especially for renewable energy sources like solar and wind power, which are highly dependent on weather patterns.
29
+
30
+ 4. **Transportation and Logistics**: Weather forecasts help transportation and logistics companies plan routes, manage schedules, and anticipate potential disruptions due to adverse weather conditions, ensuring efficient and reliable services.
31
+
32
+ 5. **Disaster Management and Emergency Response**: Accurate weather predictions aid disaster management agencies in preparing for and responding to natural disasters like hurricanes, floods, and wildfires. It allows for timely evacuations and resource allocation to mitigate the impact of extreme weather events.
33
+
34
+ 6. **Tourism and Hospitality**: The tourism industry relies on weather forecasts to manage tourist activities, plan outdoor events, and optimize resource allocation for hotels and resorts based on expected weather conditions.
35
+
36
+ 7. **Construction Industry**: Weather forecasts are essential for construction companies to plan construction schedules, especially for outdoor projects that are susceptible to weather-related delays and safety risks.
37
+
38
+ 8. **Retail and Sales**: Weather predictions impact consumer behavior, influencing purchases of seasonal items like clothing, outdoor equipment, and home appliances.
39
+
40
+ 9. **Public Health**: Weather forecasts help public health agencies anticipate and prepare for weather-related health risks, such as heatwaves, air pollution, and disease outbreaks.
41
+
42
+ 10. **Environmental Monitoring**: Weather forecasting is essential for monitoring and understanding weather-related environmental changes, including climate trends, sea-level rise, and extreme weather occurrences.
43
+
44
+ 11. **Sports and Events**: Weather forecasts play a significant role in organizing outdoor sports events, concerts, and festivals, ensuring the safety and comfort of participants and attendees.
45
+
46
+ 12. **Media and News Reporting**: Weather forecasts are frequently featured in media to keep the public informed about upcoming weather conditions and potential weather-related impacts.
47
+
48
+ Throughout the tutorial, you will learn how weather forecasting works and will have the opportunity to implement a robust weather forecasting application by yourself!.
49
+ Step by step, we will guide you through the process, ensuring that you gain a deep understanding of each concept and its practical implementation.
50
+ """
51
+ )
52
+
53
+ st.info("Click on the button below to continue!")
54
+
55
+ if st.button("Complete"):
56
+ complete_level(LEVEL)
57
+
58
+ render_page(step1_page, LEVEL)
pages/2_Step_2.py DELETED
@@ -1,16 +0,0 @@
1
- import streamlit as st
2
- from utils.levels import complete_level, render_page, initialize_level
3
-
4
- initialize_level()
5
-
6
- LEVEL = 2
7
-
8
- def step2_page():
9
- st.header("Step 2")
10
- st.subheader("Level 2: Introduction")
11
- st.write("This is the second step of the tutorial. You can add your own content here.")
12
-
13
- if st.button("Complete"):
14
- complete_level(LEVEL)
15
-
16
- render_page(step2_page, LEVEL)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pages/2_Technology Behind It.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from utils.levels import complete_level, render_page, initialize_level
3
+
4
+ initialize_level()
5
+
6
+ LEVEL = 2
7
+
8
+ def step2_page():
9
+ st.header("Technology Behind It")
10
+ st.markdown(
11
+ """
12
+ ### How machine learning is used for accurate weather forecasting?
13
+ Traditional numerical weather prediction models have been the cornerstone of meteorology for decades, relying on
14
+ complex physical equations and extensive observational data.
15
+
16
+ However, the rise of machine learning has brought a paradigm shift to weather forecasting, unlocking new possibilities and enhanced accuracy in predicting atmospheric phenomena.
17
+ Machine learning techniques leverage the power of algorithms to identify patterns and relationships within vast and complex weather datasets.
18
+ By learning from historical weather data, machine learning models can capture hidden patterns and non-linear relationships that might be
19
+ challenging to represent using traditional methods. This empowers meteorologists and researchers to create highly sophisticated and
20
+ precise forecasting models that can make predictions for various temporal scales, from short-term weather conditions to long-term climate trends.
21
+
22
+ Here's how it works:
23
+ 1. **Data Collection**: The first step is to collect large amounts of historical weather data, which includes various atmospheric parameters like temperature, humidity, wind speed, pressure, solar irradiance, and more. This data is typically obtained from weather stations, satellites, radars, and other sources.
24
+
25
+ 2. **Data Preprocessing**: The collected data needs to be preprocessed to handle missing values, outliers, and inconsistencies. It may involve data cleaning, imputation, and scaling to ensure that the data is suitable for training machine learning models.
26
+
27
+ 3. **Feature Engineering:**: In weather forecasting, relevant features play a crucial role in accurate predictions. Engineers and meteorologists work together to extract meaningful features from the raw data, which could include time-based features (e.g., day of the week, month), lagged values (to capture temporal dependencies), geographic features (latitude, longitude), and more.
28
+
29
+ 4. **Data Splitting**: The preprocessed data is split into training and testing sets. The training set is used to train the machine learning model, and the testing set is used to evaluate its performance.
30
+
31
+ 5. **Model Selection**: Different machine learning algorithms can be used for weather forecasting, such as regression models (e.g., linear regression), time series models (e.g., ARIMA, SARIMA), and more advanced models like decision trees, random forests, gradient boosting, and neural networks. The choice of model depends on the characteristics of the data and the forecasting task.
32
+
33
+ 6. **Model Training**: The selected machine learning model is trained on the training data. During training, the model learns from the historical data and tries to capture patterns and relationships between the input features (e.g., temperature, humidity) and the target variable (e.g., future temperature).
34
+
35
+ 7. **Model Evaluation**: After training, the model's performance is evaluated using the testing data. Common evaluation metrics for weather forecasting include mean absolute error (MAE), mean squared error (MSE), root mean squared error (RMSE), and correlation coefficients.
36
+
37
+ 8. **Forecasting**: Once the model is trained and evaluated, it can be used to make predictions for future weather conditions based on new input data. For example, given current weather conditions and historical data, the model can forecast the weather for the next few hours or days.
38
+
39
+ 9. **Updating and Re-training**: Weather data is constantly changing, so the forecasting model needs to be updated regularly with new data. Periodic re-training of the model ensures that it stays up-to-date and continues to make accurate predictions.
40
+
41
+ """
42
+ )
43
+
44
+ st.image(
45
+ "https://miro.medium.com/v2/resize:fit:720/format:webp/1*mRpdRbCta8VWlTrx12WNOQ.jpeg",
46
+ use_column_width=True,
47
+ )
48
+
49
+ if st.button("Complete"):
50
+ complete_level(LEVEL)
51
+
52
+ render_page(step2_page, LEVEL)
pages/3_Training the Model.py ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+ from utils.levels import complete_level, render_page, initialize_level
3
+ from utils.login import get_login, initialize_login
4
+ import streamlit as st
5
+ import pandas as pd
6
+ import numpy as np
7
+ from sklearn.model_selection import train_test_split
8
+ from sklearn.metrics import r2_score
9
+ from sklearn.ensemble import RandomForestRegressor
10
+ from sklearn.model_selection import RandomizedSearchCV
11
+ import matplotlib.pyplot as plt
12
+ from matplotlib.backends.backend_agg import RendererAgg
13
+ _lock = RendererAgg.lock
14
+ import base64
15
+ from io import BytesIO
16
+ from PIL import Image, ImageFilter
17
+ import lightgbm as lgb
18
+
19
+ initialize_login()
20
+ initialize_level()
21
+
22
+ LEVEL = 3
23
+
24
+ File_PATH = 'datasets/Building_forcasting.csv'
25
+
26
+ def process_file(csv_file):
27
+ data = pd.read_csv(csv_file, index_col='Timestamp')
28
+ data.index = pd.to_datetime(data.index)
29
+ data = data.fillna(0)
30
+ return data
31
+
32
+
33
+ def model_predict(data, model_choice, train_size, tune_model):
34
+ if model_choice == 'LightGBM':
35
+ model = lgb.LGBMRegressor() if not tune_model else lgb.LGBMRegressor(**tuned_parameters('lgbm'))
36
+ elif model_choice == 'Random Forest':
37
+ model = RandomForestRegressor(n_estimators=100, random_state=42) if not tune_model else RandomForestRegressor(**tuned_parameters('rf'))
38
+
39
+ X, y = create_model_inputs(data, 288, 288)
40
+
41
+ X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=train_size/100, random_state=42, shuffle=False)
42
+
43
+ model.fit(X_train, y_train)
44
+ y_pred = model.predict(X_test)
45
+
46
+ return y_test, y_pred, model
47
+
48
+
49
+ def create_model_inputs(data, lag, mean_period):
50
+ df_processed = data.copy()
51
+ df_processed['PV_Output_lag'] = df_processed['PV_Output'].shift(lag)
52
+ df_processed['PV_Output_mean'] = df_processed['PV_Output'].rolling(window=mean_period).mean()
53
+
54
+ X = df_processed[['Solar_Irradiance', 'Temperature', 'Rain_Fall', 'Wind_speed', 'PV_Output_lag', 'PV_Output_mean']].dropna()
55
+ y = df_processed[['PV_Output']].loc[X.index]
56
+
57
+ return X, y
58
+
59
+
60
+ def show_output(y_test, y_pred):
61
+ st.sidebar.subheader("Model Performance")
62
+ st.sidebar.write(f"Test R2 score: {r2_score(y_test, y_pred):.2f}")
63
+
64
+ fig, axs = plt.subplots(3, figsize=(12, 18))
65
+ axs[0].plot(y_test.index, y_pred/1000, label='Predicted')
66
+ axs[0].plot(y_test.index, y_test['PV_Output']/1000, label='Actual')
67
+ axs[0].legend()
68
+ axs[0].set_title('Prediction vs Actual (Solar Power Generation)')
69
+ axs[0].set_xlabel('Date')
70
+ axs[0].set_ylabel('Solar Power Generation (kW)')
71
+
72
+ axs[1].plot(y_test.index, y_pred/1000, label='Predicted')
73
+ axs[1].set_title('Predicted Solar Power Generation')
74
+ axs[1].set_xlabel('Date')
75
+ axs[1].set_ylabel('Solar Power Generation (kW)')
76
+
77
+ axs[2].plot(y_test.index, y_test['PV_Output']/1000, label='Actual')
78
+ axs[2].set_title('Actual Solar Power Generation')
79
+ axs[2].set_xlabel('Date')
80
+ axs[2].set_ylabel('Solar Power Generation (kW)')
81
+
82
+ fig.tight_layout()
83
+ with _lock:
84
+ st.pyplot(fig)
85
+
86
+ return fig
87
+
88
+
89
+ def download_link(y_test, y_pred):
90
+ y_pred_df = pd.DataFrame({'Timestamp': y_test.index, 'Predicted_Power': y_pred, 'Actual_Total_Power_(kW)': y_test['PV_Output']})
91
+ csv = y_pred_df.to_csv(index=False)
92
+ b64 = base64.b64encode(csv.encode()).decode()
93
+ href = f'<a href="data:file/csv;base64,{b64}" download="Predicted_Solar_Power.csv">Download Predicted Power CSV File</a>'
94
+ st.sidebar.markdown(href, unsafe_allow_html=True)
95
+
96
+
97
+ def feature_importance_plot(model, feature_names):
98
+ # Get feature importances
99
+ importance = model.feature_importances_
100
+ # Normalize by the sum of all importances
101
+ importance = 100.0 * (importance / importance.sum())
102
+ plt.figure(figsize=(10, 6))
103
+ plt.bar(feature_names, importance)
104
+ plt.title('Feature Importance')
105
+ plt.xlabel('Features')
106
+ plt.ylabel('Importance (%)')
107
+ return plt.gcf()
108
+
109
+
110
+ def download_plot(fig):
111
+ tmpfile = BytesIO()
112
+ fig.savefig(tmpfile, format='png')
113
+ encoded = base64.b64encode(tmpfile.getvalue()).decode('utf-8')
114
+
115
+ href = f'<a href="data:image/png;base64,{encoded}" download="plot.png">Download Result Plot</a>'
116
+ st.sidebar.markdown(href, unsafe_allow_html=True)
117
+
118
+
119
+ def tuned_parameters(model):
120
+ if model == 'lgbm':
121
+ params = {
122
+ 'num_leaves': [10, 20, 30, 40, 50],
123
+ 'max_depth': [-1, 3, 5, 10],
124
+ 'learning_rate': [0.01, 0.05, 0.1],
125
+ 'n_estimators': [100, 500, 1000]
126
+ }
127
+ return params
128
+
129
+ elif model == 'rf':
130
+ params = {
131
+ 'n_estimators': [10, 100, 500, 1000],
132
+ 'max_depth': [None, 10, 20, 30, 40, 50],
133
+ 'min_samples_split': [2, 5, 10],
134
+ 'min_samples_leaf': [1, 2, 4]
135
+ }
136
+ return params
137
+
138
+ def step3_page():
139
+ st.header("Training the Model")
140
+ st.subheader("Exploring the data")
141
+ st.title("Solar Forecasting App")
142
+
143
+ # Display the image and information in a grid layout
144
+ col1 = st.columns([1])
145
+
146
+ with col1[0]:
147
+ data = {
148
+ 'Timestamp': ['11/1/2022 0:20', '11/1/2022 0:25'],
149
+ 'Total_Power (kW)': [37337, 44590],
150
+ 'PV_Output': [296.6, 298.4],
151
+ 'Solar_Irradiance': [0, 0],
152
+ 'Temperature': [25.1, 24.7],
153
+ 'Rain_Fall': [42.6, 42.6],
154
+ 'Wind_Speed': [0.6, 0.4]
155
+ }
156
+ df = pd.DataFrame(data)
157
+ st.subheader("Example of CSV file DataFrame")
158
+ st.table(df)
159
+
160
+ csv_file = st.sidebar.file_uploader("Upload CSV", type=['csv'])
161
+
162
+ if csv_file is not None:
163
+ data = process_file(csv_file)
164
+
165
+ train_size = st.sidebar.slider("Select Train Dataset Size (%)", min_value=10, max_value=90, value=70)
166
+
167
+ models = ['LightGBM', 'Random Forest']
168
+ model_choice = st.sidebar.selectbox('Choose Model', models)
169
+
170
+ tune_model = st.sidebar.checkbox('Tune Hyperparameters')
171
+
172
+ y_test, y_pred, model = model_predict(data, model_choice, train_size, tune_model)
173
+
174
+ # Display feature importance
175
+ if st.sidebar.checkbox('Show feature importance'):
176
+ feature_names = ['Solar_Irradiance', 'Temperature', 'Rain_Fall', 'Wind_speed', 'PV_Output_lag',
177
+ 'PV_Output_mean']
178
+ fig = feature_importance_plot(model, feature_names)
179
+ with _lock:
180
+ st.pyplot(fig)
181
+
182
+ fig = show_output(y_test, y_pred)
183
+
184
+ download_link(y_test, y_pred)
185
+
186
+ download_plot(fig)
187
+
188
+ if st.button("Complete"):
189
+ complete_level(LEVEL)
190
+
191
+
192
+ render_page(step3_page, LEVEL)
pages/{3_Quiz.py β†’ 4_Quiz.py} RENAMED
File without changes
requirements.txt CHANGED
@@ -1 +1,6 @@
1
- firebase_admin
 
 
 
 
 
 
1
+ firebase_admin
2
+ scikit-learn
3
+ lightgbm
4
+ matplotlib
5
+ streamlit-pandas-profiling
6
+ pyngrok