diff --git a/app.py b/app.py
index aa23bcdd8..b53447a9c 100644
--- a/app.py
+++ b/app.py
@@ -15,6 +15,8 @@ page_label = nv()
if page_label == "Homepage":
pg.home()
+elif page_label == "BuffBot":
+ pg.buffbot()
elif page_label == "Outstanding Members":
pg.outstanding_members()
@@ -33,14 +35,16 @@ elif page_label == "Join Us":
# block of PythonX lessons
elif page_label == "About PythonX":
pg.pythonx_homepage()
-elif page_label == "Lesson1":
- pg.pythonx_lesson1()
-elif page_label == "Lesson2":
- pg.pythonx_lesson2()
-elif page_label == "Lesson3":
- pg.pythonx_lesson3()
-elif page_label == "Lesson4":
- pg.pythonx_lesson4()
+elif page_label == "Introduction":
+ pg.pythonx_introduction()
+elif page_label == "WordCloud":
+ pg.pythonx_wordcloud()
+elif page_label == "Finance":
+ pg.pythonx_finance()
+elif page_label == "GeoMap":
+ pg.pythonx_geomap()
+elif page_label == "BuffBot":
+ pg.pythonx_buffbot()
# block of CIS Tech Challenge Event
elif page_label == "CIS Tech Challenge":
diff --git a/app_config.json b/app_config.json
new file mode 100644
index 000000000..045835810
--- /dev/null
+++ b/app_config.json
@@ -0,0 +1,16 @@
+{
+ "deepseek":{
+ "api_url": "https://api.deepseek.com",
+ "api_key": "sk-12165b127043441697a8940918e207ac"
+ },
+
+ "ollama":{
+ "api_url": "http://localhost:11434/v1",
+ "api_key": "ollama"
+ },
+
+ "send_email": {
+ "sender_email": "noreply@buffteks.org",
+ "password": "cidm4360fall2024@*"
+ }
+}
\ No newline at end of file
diff --git a/chatbot_config.json b/chatbot_config.json
deleted file mode 100644
index ddaef19c7..000000000
--- a/chatbot_config.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "api_url": "https://api.deepseek.com",
- "api_key": "sk-12165b127043441697a8940918e207ac",
- "ollama_api_url": "http://localhost:11434/v1",
- "ollama_api_key": "ollama"
-}
\ No newline at end of file
diff --git a/deepseek_chatbot.py b/deepseek_chatbot.py
index 7cdf87e45..0113cf52a 100644
--- a/deepseek_chatbot.py
+++ b/deepseek_chatbot.py
@@ -12,7 +12,7 @@ with st.expander("See Source Code"):
st.code(f.read(), language="python")
# Load API credentials from config.json
-with open('chatbot_config.json') as config_file:
+with open('app_config.json') as config_file:
config = json.load(config_file)
openai_api_base_url = config["api_url"]
openai_api_key = config["api_key"]
diff --git a/llama_chatbot.py b/llama_chatbot.py
index b51b43523..989925550 100644
--- a/llama_chatbot.py
+++ b/llama_chatbot.py
@@ -15,14 +15,14 @@ st.markdown("
BuffBot🦬
",
# st.subheader()
st.info("Powered by llama3.2:1b model via [Ollama](https://ollama.com/library/llama3.2:1b)!")
with st.expander("See Source Code"):
- with open(__file__, "r") as f:
- st.code(f.read(), language="python")
+ with open(__file__, "r") as f:
+ st.code(f.read(), language="python")
# Load API credentials from config.json
-with open('chatbot_config.json') as config_file:
+with open('app_config.json') as config_file:
config = json.load(config_file)
- api_base_url = config["ollama_api_url"]
- api_key = config["ollama_api_key"]
+ api_base_url = config["ollama"]["api_url"]
+ api_key = config["ollama"]["api_key"]
client = OpenAI(api_key=api_key, base_url=api_base_url)
diff --git a/new_members.json b/new_members.json
new file mode 100644
index 000000000..ab4200988
--- /dev/null
+++ b/new_members.json
@@ -0,0 +1,3 @@
+[
+
+]
\ No newline at end of file
diff --git a/webpages/SendEmail.py b/webpages/SendEmail.py
index 035cc6948..67dfd9836 100644
--- a/webpages/SendEmail.py
+++ b/webpages/SendEmail.py
@@ -106,7 +106,9 @@ def send_email(sender_email, password, to_email, subject):
server.starttls() # Enable TLS encryption for secure connection
server.login(sender_email, password) # Log in with the sender's email and password
server.send_message(message) # Send the email message
+
print("Email sent successfully!")
+
except Exception as e:
# Catch and display any exceptions that occur during the sending process
print(f"Failed to send email: {str(e)}")
diff --git a/webpages/__init__.py b/webpages/__init__.py
index 4dc3667e9..30ecd37ad 100644
--- a/webpages/__init__.py
+++ b/webpages/__init__.py
@@ -6,10 +6,11 @@ from .join_us import join_us
from .testing import testing
from .reference import reference
from .pythonx_lessons_pages.pythonx_homepage import pythonx_homepage
-from .pythonx_lessons_pages.pythonx_lesson1 import pythonx_lesson1
-from .pythonx_lessons_pages.pythonx_lesson2 import pythonx_lesson2
-from .pythonx_lessons_pages.pythonx_lesson3 import pythonx_lesson3
-from .pythonx_lessons_pages.pythonx_lesson4 import pythonx_lesson4
+from .pythonx_lessons_pages.pythonx_introduction import pythonx_introduction
+from .buff_bot import buffbot
+from .pythonx_lessons_pages.pythonx_finance import pythonx_finance
+from .pythonx_lessons_pages.pythonx_geomap import pythonx_geomap
+from .pythonx_lessons_pages.pythonx_wordcloud import pythonx_wordcloud
from .outstanding_members import outstanding_members
from .cis_tech_challenge_pages.cis_tech_challenge_homepage import cis_tech_challenge_homepage
from .coreteks_pages.coreteks_homepage import coreteks_homepage
diff --git a/webpages/__pycache__/SendEmail.cpython-312.pyc b/webpages/__pycache__/SendEmail.cpython-312.pyc
index 85d777c1d..9d086e0bf 100644
Binary files a/webpages/__pycache__/SendEmail.cpython-312.pyc and b/webpages/__pycache__/SendEmail.cpython-312.pyc differ
diff --git a/webpages/__pycache__/__init__.cpython-312.pyc b/webpages/__pycache__/__init__.cpython-312.pyc
index 348dc8202..993fc751f 100644
Binary files a/webpages/__pycache__/__init__.cpython-312.pyc and b/webpages/__pycache__/__init__.cpython-312.pyc differ
diff --git a/webpages/__pycache__/buff_bot.cpython-312.pyc b/webpages/__pycache__/buff_bot.cpython-312.pyc
new file mode 100644
index 000000000..056d479c4
Binary files /dev/null and b/webpages/__pycache__/buff_bot.cpython-312.pyc differ
diff --git a/webpages/__pycache__/home.cpython-312.pyc b/webpages/__pycache__/home.cpython-312.pyc
index 72181dcae..8591948ab 100644
Binary files a/webpages/__pycache__/home.cpython-312.pyc and b/webpages/__pycache__/home.cpython-312.pyc differ
diff --git a/webpages/__pycache__/join_us.cpython-312.pyc b/webpages/__pycache__/join_us.cpython-312.pyc
index 6f80101f4..2078f6648 100644
Binary files a/webpages/__pycache__/join_us.cpython-312.pyc and b/webpages/__pycache__/join_us.cpython-312.pyc differ
diff --git a/webpages/__pycache__/navigation.cpython-312.pyc b/webpages/__pycache__/navigation.cpython-312.pyc
index b38aef41c..dc8e3cec9 100644
Binary files a/webpages/__pycache__/navigation.cpython-312.pyc and b/webpages/__pycache__/navigation.cpython-312.pyc differ
diff --git a/webpages/__pycache__/outstanding_members.cpython-312.pyc b/webpages/__pycache__/outstanding_members.cpython-312.pyc
index a61b9a970..600e97888 100644
Binary files a/webpages/__pycache__/outstanding_members.cpython-312.pyc and b/webpages/__pycache__/outstanding_members.cpython-312.pyc differ
diff --git a/webpages/buff_bot.py b/webpages/buff_bot.py
new file mode 100644
index 000000000..2ac01ee80
--- /dev/null
+++ b/webpages/buff_bot.py
@@ -0,0 +1,119 @@
+import streamlit as st
+from openai import OpenAI # OpenAI compatibility
+import json
+# reference:
+# - Use OpenAI to connect Ollama: https://ollama.com/blog/openai-compatibility
+# - Build Chatbot with streamlit: https://streamlit.io/generative-ai
+# - Ollama docker: https://hub.docker.com/r/ollama/ollama
+# - [TBD] Finetune: https://docs.loopin.network/tutorials/LLM/llama3-finetune
+
+
+# Clear chat history
+def clear_chat():
+ st.session_state.messages = []
+ st.toast("Chat Cleaned", icon="🧹")
+
+def buffbot():
+ # Set up the Streamlit app
+ st.markdown("BuffBot🦬
", unsafe_allow_html=True)
+ st.markdown(" Your friendly AI chatbot powered by LLM! 🤖 ", unsafe_allow_html=True)
+ # Display info and source code
+ with st.expander("See Source Code"):
+ with open(__file__, "r", encoding="utf-8") as f:
+ st.code(f.read(), language="python")
+
+ # Select AI model for chatbot
+ model_options = ["llama3.2:1b", "deepseek-chat", ]
+ # on_change callback to clear chat history when model is changed
+ selected_model = st.selectbox("**👉 Please select a model to start**", model_options, on_change=clear_chat)
+
+ # Initialize session state to store chat history and message count
+ if "messages" not in st.session_state:
+ st.session_state.messages = []
+ # Initialize message count
+ if "message_count" not in st.session_state:
+ st.session_state.message_count = 0
+
+ # Load API credentials from config.json
+ # the config file contains the API key and base URL for the selected model
+ """
+ {
+ "deepseek":{
+ "api_url": "https://api.deepseek.com",
+ "api_key": "YOUR_API_KEY"
+ },
+
+ "ollama":{
+ "api_url": "http://localhost:11434/v1",
+ "api_key": "ollama"
+ }
+ }
+ """
+ # The API key and base URL are loaded based on the selected model
+ with open('app_config.json') as config_file:
+ config = json.load(config_file)
+ # deepseek-chat model, online API
+ if selected_model == "deepseek-chat":
+ api_base_url = config["deepseek"]["api_url"]
+ api_key = config["deepseek"]["api_key"]
+ st.info("Powered by the online [DeepSeek](https://www.deepseek.com/) API!\
+ Just a heads up, you have 10 messages to use.")
+ # Set the maximum number of user messages
+ MAX_USER_MESSAGES = 10
+
+ # llama3.2:1b model, local API
+ if selected_model == "llama3.2:1b":
+ api_base_url = config["ollama"]["api_url"]
+ api_key = config["ollama"]["api_key"]
+ st.info("Powered by local llama3.2:1b model via [Ollama](https://ollama.com/library/llama3.2:1b)!\
+ Just a heads up, you have 100 messages to use.")
+ MAX_USER_MESSAGES = 100
+
+ # Initialize OpenAI client to connect with the selected model API
+ client = OpenAI(api_key=api_key, base_url=api_base_url)
+
+ # print welcome message
+ with st.chat_message("assistant", avatar="🦬"):
+ st.markdown("Welcome to BuffBot! What Can I Do for You Today?🌞")
+
+ # Display chat history with different avatars for user and AI assistant
+ for message in st.session_state.messages:
+ if message["role"] == "user":
+ avatar="🤠"
+ else:
+ avatar="🦬"
+ with st.chat_message(message["role"], avatar=avatar):
+ st.markdown(message["content"])
+
+ # Get user input
+ if prompt := st.chat_input("Type your message here..."):
+ # Add user message to chat history
+ st.session_state.messages.append({"role": "user", "content": prompt})
+ # Display user message with cowboy avatar
+ with st.chat_message("user", avatar="🤠"):
+ st.markdown(prompt)
+
+ # Generate reply
+ with st.chat_message("assistant", avatar="🦬"):
+ with st.spinner('Thinking...'):
+ # Call the selected model API to generate a response
+ stream = client.chat.completions.create(
+ model=selected_model,
+ messages=[
+ {"role": m["role"], "content": m["content"]}
+ for m in st.session_state.messages
+ ],
+ stream=True, # stream the response
+ )
+ # Display the response from the model API
+ response = st.write_stream(stream)
+ # Add the AI assistant response to the chat history
+ st.session_state.messages.append({"role": "assistant", "content": response})
+
+ # Clear chat history
+ if st.button("Clear Chat"):
+ clear_chat()
+ st.rerun()
+
+
+
diff --git a/webpages/coreteks_pages/__pycache__/coreteks_homepage.cpython-312.pyc b/webpages/coreteks_pages/__pycache__/coreteks_homepage.cpython-312.pyc
index 4f67dc5c7..f3b25c25b 100644
Binary files a/webpages/coreteks_pages/__pycache__/coreteks_homepage.cpython-312.pyc and b/webpages/coreteks_pages/__pycache__/coreteks_homepage.cpython-312.pyc differ
diff --git a/webpages/coreteks_pages/coreteks_homepage.py b/webpages/coreteks_pages/coreteks_homepage.py
index 03d980e8d..d24545bc7 100644
--- a/webpages/coreteks_pages/coreteks_homepage.py
+++ b/webpages/coreteks_pages/coreteks_homepage.py
@@ -27,7 +27,7 @@ def coreteks_homepage():
# for linux talk
st.subheader(" :two: Linux Security and Web Server Setup")
- st.image("./images/11-19-2024-CaddyAndLinux.jpg", width= 500)
+ st.image("./images/11-19-2024-CaddyAndLinux.jpg", width= 700)
st.link_button(label="Watch Video Recording", use_container_width=True, type="primary", url="https://wtamu0-my.sharepoint.com/:v:/g/personal/czhang_wtamu_edu/EdgZHrnkqihNrOZGYAT6O2MBSRBOBMv3czD_uIE21KgsWw?nav=eyJyZWZlcnJhbEluZm8iOnsicmVmZXJyYWxBcHAiOiJPbmVEcml2ZUZvckJ1c2luZXNzIiwicmVmZXJyYWxBcHBQbGF0Zm9ybSI6IldlYiIsInJlZmVycmFsTW9kZSI6InZpZXciLCJyZWZlcnJhbFZpZXciOiJNeUZpbGVzTGlua0NvcHkifX0&e=obIoyY", )
st.link_button(label="Download PDF Instruction", use_container_width=True, type="primary", url="https://wtamu0-my.sharepoint.com/:b:/g/personal/czhang_wtamu_edu/Eaygqoc_FqxNgYj3BlEOXhsBzOj-BWTbNKwkpJEYSDBwgA?e=K7qxSU", )
st.divider()
diff --git a/webpages/home.py b/webpages/home.py
index 02d9e33da..422bb9530 100644
--- a/webpages/home.py
+++ b/webpages/home.py
@@ -1,4 +1,136 @@
import streamlit as st
+from .join_us import join_us
def home():
- st.html("./webpages/buffteks.html")
\ No newline at end of file
+
+ # Custom CSS to style the page
+ st.markdown("""
+
+ """, unsafe_allow_html=True)
+
+ # Header
+ st.markdown("""
+
+ """, unsafe_allow_html=True)
+
+
+ # Our Mission
+ st.markdown("""
+
+ Our Mission
+ Empower members with advanced software development knowledge, foster new skills and technologies in a collaborative and engaging community.
+
+ """, unsafe_allow_html=True)
+
+ # Our Activities
+ st.markdown("""
+
+ Our Activities
+
+ - BuffTeks Project: Faculty-led coding projects to provide IT solutions and support to problems facing local communities as part of an experiential learning effort.
+ - BuffTeks Classroom: An open learning platform devoted to sharing knowledge of information technology including Python programming and web application development.
+ - BuffTeks Event: Host competitions and hackathons that empower students to use classroom knowledge for real-world solutions.
+
+
+ """, unsafe_allow_html=True)
+
+ # Faculty Advisors
+ st.markdown("""
+
+ Faculty Advisors
+
+
+
+

+
+ Dr. Jeffry Babb
+ BuffTeks Founder
+
+
+
+

+
+
+
+

+
+
+
+
+
+ """, unsafe_allow_html=True)
diff --git a/webpages/join_us.py b/webpages/join_us.py
index 76deec215..3399ae917 100644
--- a/webpages/join_us.py
+++ b/webpages/join_us.py
@@ -1,50 +1,103 @@
import streamlit as st
from .SendEmail import send_email
import time
+import json
+import re
+# reset all inputs
+def clear_text():
+ st.session_state["full_name"] = ""
+ st.session_state["email"] = ""
+ st.session_state["stu_major"] = None
+ st.session_state["other_major"] = ""
+ st.session_state["stu_year"] = None
+ st.session_state["skills_selection"] = []
+ st.session_state["other_skill"] = ""
+def is_valid_email(email):
+ pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
+ return re.match(pattern, email)
+
+@st.dialog("Review Your Information")
+def review(review_info):
+ st.write(review_info)
+ confirm_btn = st.button("Confirm and Send Email", on_click=clear_text)
+ if confirm_btn:
+ # save new member info to json file
+ try:
+ with open('new_members.json', 'r+') as file:
+ data = json.load(file)
+ data.append(review_info)
+ file.seek(0)
+ json.dump(data, file, indent=4)
+ except FileNotFoundError:
+ with open('new_members.json', 'w') as file:
+ json.dump([refiew_info], file, indent=4)
+
+ # load email config from app_config.json
+ with open('app_config.json') as config_file:
+ config = json.load(config_file)
+ email_config = config["send_email"]
+
+ # send email to new member
+ with st.spinner('📧 Sending Email...'):
+ send_email(
+ sender_email=email_config["sender_email"],
+ password=email_config["password"],
+ to_email=review_info["Email"],
+ subject=f"Welcome to join BuffTeks, {review_info["Full Name"]}!"
+ )
+ time.sleep(3)
+ st.success("🎉Thanks for submitting your information, you will receive an invitation email shortly. \
+ \nPlease contact Dr.Zhang (czhang@wtamu.edu) if you need any help.")
+
+ progress_text = "⏱️ Window will be closed in 10 seconds..."
+ my_bar = st.progress(0, text=progress_text)
+ for percent_complete in range(99):
+ time.sleep(0.1)
+ my_bar.progress(percent_complete + 1, text=progress_text)
+ time.sleep(1)
+ st.rerun()
+
+
def join_us():
st.title("Join Us")
st.write("Thank you for your interest in the BuffTeks student organization! Please share your information below, \
and our Faculty Advisor will send you an invitation to join our Discord channel. \
If you have any questions, feel free to contact our Faculty Advisor, Dr. Carl Zhang, at czhang@wtamu.edu.")
- full_name = st.text_input("**Full Name**")
- wt_email = st.text_input("**WT Email**")
+ full_name = st.text_input("**Full Name**", key="full_name")
+ email = st.text_input("**Email**", key="email", placeholder="WT email is preferred")
major_list = ["Accounting", "Computer Infromation Systems", "Economics", "Finance", "General Business", "Management", "Marketing", "Other"]
- stu_major = st.radio("**Major/Field of Study**",major_list)
+ stu_major = st.radio("**Major/Field of Study**",major_list, key="stu_major")
if stu_major == "Other":
- other_major = st.text_input("Please specify the other major")
+ other_major = st.text_input("Please specify the other major", key="other_major")
stu_major = other_major
year_of_study_list = ["Freshman", "Sophomore", "Junior", "Senior", "Graduate"]
- stu_year = st.radio("**Year of Study**", year_of_study_list)
+ stu_year = st.radio("**Year of Study**", year_of_study_list, key="stu_year")
skills_options = ["Programming", "Cybersecurity", "Web Development", "Mobile App Development", "machine Learning/AI", "Data Analysis", "Other"]
skills_selection = st.multiselect(
label= "**Technical Skills You Are Interested In (Please check all that apply)**",
options = skills_options,
- placeholder = "Choose all that apply")
+ placeholder = "Choose all that apply",
+ key="skills_selection")
if "Other" in skills_selection:
- other_skill = st.text_input("**Please specify the other skills**")
+ other_skill = st.text_input("**Please specify the other skills**", key="other_skill")
skills_selection.remove("Other")
- skills_selection.append(f"Other ({other_skill})")
+ skills_selection.append(f"{other_skill}")
next_btn = st.button("Next")
-
if next_btn:
- if "wtamu.edu" not in wt_email:
- st.warning("Please input your WT Email address to receive invitation")
- with st.spinner('Sending Email...'):
- send_email(sender_email="noreply@buffteks.org", password="cidm4360fall2024@*", to_email=wt_email, subject=f"Welcome to join ButtTeks, {full_name}!")
- st.balloons()
- st.success("Thanks for submitting your information, you will receive an invitation email shortly. \n Please contact Dr.Zhang (czhang@wtamu.edu) if you need any help.")
-
-# @st.dialog("Check Email")
-# def confirm_email(wt_email, full_name):
-# st.write(f"Please double-check your **WT Email** in order to receive our invitation: \n {wt_email}")
-# if st.button("Confirm & Submit"):
-# send_email(sender_email="noreply@buffteks.org", password="cidm4360fall2024@*", to_email=wt_email, subject=f"Welcome to join ButtTeks, {full_name}!")
-# st.rerun()
-# return True
-
\ No newline at end of file
+ if not is_valid_email(email):
+ st.error("Please enter a valid email address.")
+ return
+ st.session_state.review_info = {
+ "Full Name": full_name,
+ "Email": email,
+ "Major/Field of Study": stu_major,
+ "Year of Study": stu_year,
+ "Technical Skills": skills_selection
+ }
+ review(review_info=st.session_state.review_info)
diff --git a/webpages/navigation.py b/webpages/navigation.py
index ed6086d71..9242b6ede 100644
--- a/webpages/navigation.py
+++ b/webpages/navigation.py
@@ -13,23 +13,25 @@ def navigation_bar():
page_label = sac.menu([
sac.MenuItem('Homepage', icon='house'),
+ sac.MenuItem('BuffBot', icon='robot'),
sac.MenuItem('Outstanding Members', icon='award'),
+ sac.MenuItem("Join Us", icon='person-add'),
sac.MenuItem('BuffTeks Project', icon='bi bi-laptop'),
- sac.MenuItem('BuffTeks Event', icon='calendar-event', children=[
- sac.MenuItem('CIS Tech Challenge', icon='bi bi-trophy'),
- ]),
sac.MenuItem('BuffTeks Classroom', icon='book', children=[
sac.MenuItem('About Classroom', icon='question-circle'),
sac.MenuItem('PythonX', icon='bi bi-filetype-py', children=[
sac.MenuItem('About PythonX', icon='question-circle'),
- sac.MenuItem('Lesson1', icon='1-square'),
- sac.MenuItem('Lesson2', icon='2-square'),
- sac.MenuItem('Lesson3', icon='3-square'),
- sac.MenuItem('Lesson4', icon='4-square'),
+ sac.MenuItem('Introduction', icon='1-square'),
+ sac.MenuItem('WordCloud', icon='2-square'),
+ sac.MenuItem('Finance', icon='3-square'),
+ sac.MenuItem('GeoMap', icon='4-square'),
+ # sac.MenuItem('BuffBot', icon='5-square'),
]),
sac.MenuItem('CoreTeks', icon='bi bi-tools'),
]),
- sac.MenuItem("Join Us", icon='person-add'),
+ sac.MenuItem('BuffTeks Event', icon='calendar-event', children=[
+ sac.MenuItem('CIS Tech Challenge', icon='bi bi-trophy'),
+ ]),
# sac.MenuItem("Testing", icon='fingerprint'),
# sac.MenuItem(type='divider'),
# sac.MenuItem('Link', type='group', children=[
diff --git a/webpages/pythonx_lessons_pages/__pycache__/pythonx_buffbot.cpython-312.pyc b/webpages/pythonx_lessons_pages/__pycache__/pythonx_buffbot.cpython-312.pyc
new file mode 100644
index 000000000..ee1f2c4b4
Binary files /dev/null and b/webpages/pythonx_lessons_pages/__pycache__/pythonx_buffbot.cpython-312.pyc differ
diff --git a/webpages/pythonx_lessons_pages/__pycache__/pythonx_finance.cpython-312.pyc b/webpages/pythonx_lessons_pages/__pycache__/pythonx_finance.cpython-312.pyc
new file mode 100644
index 000000000..224f64aab
Binary files /dev/null and b/webpages/pythonx_lessons_pages/__pycache__/pythonx_finance.cpython-312.pyc differ
diff --git a/webpages/pythonx_lessons_pages/__pycache__/pythonx_geomap.cpython-312.pyc b/webpages/pythonx_lessons_pages/__pycache__/pythonx_geomap.cpython-312.pyc
new file mode 100644
index 000000000..30426ed8e
Binary files /dev/null and b/webpages/pythonx_lessons_pages/__pycache__/pythonx_geomap.cpython-312.pyc differ
diff --git a/webpages/pythonx_lessons_pages/__pycache__/pythonx_introduction.cpython-312.pyc b/webpages/pythonx_lessons_pages/__pycache__/pythonx_introduction.cpython-312.pyc
new file mode 100644
index 000000000..79cf90f16
Binary files /dev/null and b/webpages/pythonx_lessons_pages/__pycache__/pythonx_introduction.cpython-312.pyc differ
diff --git a/webpages/pythonx_lessons_pages/__pycache__/pythonx_wordcloud.cpython-312.pyc b/webpages/pythonx_lessons_pages/__pycache__/pythonx_wordcloud.cpython-312.pyc
new file mode 100644
index 000000000..64d900663
Binary files /dev/null and b/webpages/pythonx_lessons_pages/__pycache__/pythonx_wordcloud.cpython-312.pyc differ
diff --git a/webpages/pythonx_lessons_pages/pythonx_lesson3.py b/webpages/pythonx_lessons_pages/pythonx_finance.py
similarity index 99%
rename from webpages/pythonx_lessons_pages/pythonx_lesson3.py
rename to webpages/pythonx_lessons_pages/pythonx_finance.py
index bdbaff617..213087ce3 100644
--- a/webpages/pythonx_lessons_pages/pythonx_lesson3.py
+++ b/webpages/pythonx_lessons_pages/pythonx_finance.py
@@ -4,7 +4,7 @@ import yfinance as yf
import plotly.express as px
from webpages import code_editor as ce
-def pythonx_lesson3():
+def pythonx_finance():
st.title("Lesson 3: Collecting Stock Data Through API")
st.header(":one: What is API")
diff --git a/webpages/pythonx_lessons_pages/pythonx_lesson4.py b/webpages/pythonx_lessons_pages/pythonx_geomap.py
similarity index 63%
rename from webpages/pythonx_lessons_pages/pythonx_lesson4.py
rename to webpages/pythonx_lessons_pages/pythonx_geomap.py
index 48240dd02..181d800e0 100644
--- a/webpages/pythonx_lessons_pages/pythonx_lesson4.py
+++ b/webpages/pythonx_lessons_pages/pythonx_geomap.py
@@ -1,17 +1,17 @@
-
import streamlit as st
-import pandas as pd
-import folium
-from streamlit_folium import folium_static
-from geopy.geocoders import Nominatim
-import sqlite3
-from datetime import datetime
-from folium.features import CustomIcon
+import pandas as pd # package for database connection
+import folium # package for creating maps
+from streamlit_folium import folium_static # package for displaying maps on streamlit
+from geopy.geocoders import Nominatim # package for geolocation conversion
+import sqlite3 # package for database connection
+from datetime import datetime # package for timestamp
+from folium.features import CustomIcon # package for custom icons on map
-def pythonx_lesson4():
+def pythonx_geomap():
# Initialize the database
conn = sqlite3.connect('./files/student_locations.db')
+ # Create a table to store student locations
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS students
(id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -24,16 +24,40 @@ def pythonx_lesson4():
conn.commit()
- st.title('Where are Members From')
-
+ st.title("Lesson 4: Geographical Data Visualization")
+ st.markdown("""
+ This lesson demonstrates how to handle geolocation data and visualize it using maps in Python.
+
+ The main functionalities include:
+
+ - **User Input for Location**: Users can input their city and state or city and country through a form.
+
+ - **Geolocation Processing**: The input location is processed to obtain latitude and longitude coordinates.
+
+ - **Data Storage**: The processed location data is saved to a [SQLite](https://www.geeksforgeeks.org/python-sqlite/) database.
+
+ - **Map Visualization**: All stored student locations are displayed on an interactive map.
+
+ - **Statistics Display**: The code also provides statistics on the total number of students, unique cities, and unique countries, along with the top 5 cities.
+ """)
+
+ # Display the source code
+ with st.expander("See Source Code"):
+ with open(__file__, "r", encoding="utf-8") as f:
+ st.code(f.read(), language="python")
+
+ st.subheader("Student Location Tracker")
# Input form for student location
with st.form("student_form"):
input_city = st.text_input("Enter your City, State (e.g.: Amarillo,TX), or City, Country (Toronto, Canada):")
submitted = st.form_submit_button("Submit")
- if submitted:
+ if submitted:
+ # converting addresses (like "Mountain View, CA") into geographic
+ # coordinates (like latitude 37.423021 and longitude -122.083739)
lat, lon, city, state, country = get_location(input_city)
+ # Save the location data to the database
if lat and lon:
save_student(conn, city, state, country, lat, lon)
st.success(f"Location saved: {city}, {country}")
@@ -66,7 +90,7 @@ def pythonx_lesson4():
conn.close()
-
+# convert address to latitude and longitude
def get_location(city):
geolocator = Nominatim(user_agent="student_location_app")
try:
@@ -82,7 +106,7 @@ def get_location(city):
st.write(e)
return None, None, None, None, None
-
+# save student location to database
def save_student(conn, city, state, country, lat, lon):
c = conn.cursor()
timestamp = datetime.now()
@@ -90,11 +114,14 @@ def save_student(conn, city, state, country, lat, lon):
(city, state, country, lat, lon, timestamp))
conn.commit()
+# get all student locations from database
def get_all_students(conn):
df = pd.read_sql_query("SELECT * from students", conn)
return df
+# create map with student locations
def create_map(df):
+ # Create a map centered at the US
m = folium.Map(location=[41.2706, -97.1749], zoom_start=4)
# Group by city and count occurrences
@@ -104,6 +131,7 @@ def create_map(df):
max_count = city_counts['count'].max()
min_size, max_size = 5, 20 # min and max marker sizes
+ # Add markers for each city
for _, row in city_counts.iterrows():
# Create a custom icon
icon = CustomIcon(
@@ -112,7 +140,7 @@ def create_map(df):
icon_anchor=(15, 30), # Adjust anchor point if needed
popup_anchor=(0, -30) # Adjust popup anchor if needed
)
-
+ # Calculate marker size based on count
folium.Marker(
location=[row['latitude'], row['longitude']],
popup=f"{row['city']}
{row['count']}",
diff --git a/webpages/pythonx_lessons_pages/pythonx_lesson1.py b/webpages/pythonx_lessons_pages/pythonx_introduction.py
similarity index 99%
rename from webpages/pythonx_lessons_pages/pythonx_lesson1.py
rename to webpages/pythonx_lessons_pages/pythonx_introduction.py
index 485ac3e6f..969f9ea1a 100644
--- a/webpages/pythonx_lessons_pages/pythonx_lesson1.py
+++ b/webpages/pythonx_lessons_pages/pythonx_introduction.py
@@ -3,7 +3,7 @@ from webpages import code_editor as ce
-def pythonx_lesson1():
+def pythonx_introduction():
st.title("Lesson 1: Introduction to Python")
# Lesson1-Part1: what is programming
st.header(":one: From Idea to Program")
diff --git a/webpages/pythonx_lessons_pages/pythonx_lesson2.py b/webpages/pythonx_lessons_pages/pythonx_wordcloud.py
similarity index 99%
rename from webpages/pythonx_lessons_pages/pythonx_lesson2.py
rename to webpages/pythonx_lessons_pages/pythonx_wordcloud.py
index c6aece0ee..5439e3b86 100644
--- a/webpages/pythonx_lessons_pages/pythonx_lesson2.py
+++ b/webpages/pythonx_lessons_pages/pythonx_wordcloud.py
@@ -4,7 +4,7 @@ from wordcloud import WordCloud
import matplotlib.pyplot as plt
-def pythonx_lesson2():
+def pythonx_wordcloud():
st.title("Lesson 2: Create WordClouds in Python")
st.header(":one: What is WordClouds")
st.markdown("""