From ab0e64777efda43d279ac6e5fb9bb34229acb5fe Mon Sep 17 00:00:00 2001 From: BuffTechTalk Date: Fri, 10 Jan 2025 14:29:06 -0600 Subject: [PATCH] add a AI chatbot, rename course names --- app.py | 20 +-- app_config.json | 16 +++ chatbot_config.json | 6 - deepseek_chatbot.py | 2 +- llama_chatbot.py | 10 +- new_members.json | 3 + webpages/SendEmail.py | 2 + webpages/__init__.py | 9 +- .../__pycache__/SendEmail.cpython-312.pyc | Bin 4375 -> 4375 bytes webpages/__pycache__/__init__.cpython-312.pyc | Bin 1034 -> 1087 bytes webpages/__pycache__/buff_bot.cpython-312.pyc | Bin 0 -> 5033 bytes webpages/__pycache__/home.cpython-312.pyc | Bin 365 -> 4915 bytes webpages/__pycache__/join_us.cpython-312.pyc | Bin 3084 -> 6208 bytes .../__pycache__/navigation.cpython-312.pyc | Bin 2222 -> 2300 bytes .../outstanding_members.cpython-312.pyc | Bin 3430 -> 3426 bytes webpages/buff_bot.py | 119 ++++++++++++++++ .../coreteks_homepage.cpython-312.pyc | Bin 2970 -> 2965 bytes webpages/coreteks_pages/coreteks_homepage.py | 2 +- webpages/home.py | 134 +++++++++++++++++- webpages/join_us.py | 101 +++++++++---- webpages/navigation.py | 18 +-- .../pythonx_buffbot.cpython-312.pyc | Bin 0 -> 4906 bytes .../pythonx_finance.cpython-312.pyc | Bin 0 -> 7136 bytes .../pythonx_geomap.cpython-312.pyc | Bin 0 -> 7818 bytes .../pythonx_introduction.cpython-312.pyc | Bin 0 -> 22680 bytes .../pythonx_wordcloud.cpython-312.pyc | Bin 0 -> 4096 bytes ...{pythonx_lesson3.py => pythonx_finance.py} | 2 +- .../{pythonx_lesson4.py => pythonx_geomap.py} | 58 ++++++-- ...onx_lesson1.py => pythonx_introduction.py} | 2 +- ...ythonx_lesson2.py => pythonx_wordcloud.py} | 2 +- 30 files changed, 430 insertions(+), 76 deletions(-) create mode 100644 app_config.json delete mode 100644 chatbot_config.json create mode 100644 new_members.json create mode 100644 webpages/__pycache__/buff_bot.cpython-312.pyc create mode 100644 webpages/buff_bot.py create mode 100644 webpages/pythonx_lessons_pages/__pycache__/pythonx_buffbot.cpython-312.pyc create mode 100644 webpages/pythonx_lessons_pages/__pycache__/pythonx_finance.cpython-312.pyc create mode 100644 webpages/pythonx_lessons_pages/__pycache__/pythonx_geomap.cpython-312.pyc create mode 100644 webpages/pythonx_lessons_pages/__pycache__/pythonx_introduction.cpython-312.pyc create mode 100644 webpages/pythonx_lessons_pages/__pycache__/pythonx_wordcloud.cpython-312.pyc rename webpages/pythonx_lessons_pages/{pythonx_lesson3.py => pythonx_finance.py} (99%) rename webpages/pythonx_lessons_pages/{pythonx_lesson4.py => pythonx_geomap.py} (63%) rename webpages/pythonx_lessons_pages/{pythonx_lesson1.py => pythonx_introduction.py} (99%) rename webpages/pythonx_lessons_pages/{pythonx_lesson2.py => pythonx_wordcloud.py} (99%) 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 85d777c1df7bac7b7c5a63e1e96868c925318b73..9d086e0bf91319a6356a84b67f5e65d2f1551f20 100644 GIT binary patch delta 67 zcmbQPG+l}BG%qg~0}#0DHKq#*ZRESj&&WOb1;2|p7Xv5X1wM_d9Gc&`nK?B-FaU|) UU-%|F3!LW6W@I!hQUdA%073T<`v3p{ delta 67 zcmbQPG+l}BG%qg~0}xcm#isuj+{ky6pOJI&3w{@I4hBxX3w#<^IW)g>GIMHvU;q-o Uzi>@<7C6nB$;fC}qy*Fj0Bzh6)Bpeg diff --git a/webpages/__pycache__/__init__.cpython-312.pyc b/webpages/__pycache__/__init__.cpython-312.pyc index 348dc8202356bff203fe33df90bc896c9da2f500..993fc751f04ccfd948cdeb64da68e93855cbb4f0 100644 GIT binary patch delta 332 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 E0|QLTkN^Mx delta 251 zcmdn2_Lhn7G%qg~0}!lBHB9$on8+t#Dg@+BXGmd4Va#EOVoYIbVTfX?WYlDS2~y^l z3?gBG8OQ+P&w3NfRZUkjLc|#vDj77H{Hi4M^vhF|3KG*(i}jOA)6z;(vy1gIN^)~F znQk!^m)v3ju_rSLxfgOm%_-&wG8!1}u&}keH@aWoQM<~bUc?R*(`3HISzJ<-nwXoD zSyIFT6e$AfDPjW=AO%GnK;jpNO>TZlX-=wL5jT(va!xTXkodsN$jEq`LF_XNBcldb FIRFvxG~oaM diff --git a/webpages/__pycache__/join_us.cpython-312.pyc b/webpages/__pycache__/join_us.cpython-312.pyc index 6f80101f49e14ed61310af1fb059c7e1dba1e430..2078f6648bc32ad3edf2dc00c6d38d2b0773777d 100644 GIT binary patch literal 6208 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 delta 1339 zcmX|BPfXiZ9DdJEVmtpFNHCDm;Q|8!EU=bV-P&zzDqTU^9~QI`HW0R|_|o*mLe zL2%d((K<2u4n?Zij%$@3D($!(cG)hyq)QK#n;oign6$&#*i?yK_6z|_e!us7?|a|( z^Y^p;8F5m3{zlE13y)hWQh`tK?c^=^23x zi``A$lKbS0hpzg_qS)Q;FL7$%L9oI$GK|gdF)pl)Y-GB-{mR z(+*3-P^`WAM95q6VoME|;3~u|79_8_J4o5%rxZT26p9SC93#&G2&~p)6I4lUEkU)d z1So6!4*MWlagl_loBSRMGZjFlf?*ildLAaFP@tm#y1{F}gn0&n8A=1etr4g+vYDkJ}OmwnS2vzcc+zQ1Ctx!%jAAohdv@j)Xo@d zyLDwA={9F%EzQW7wlAwIDYRthD#GM$;z-RE zFMOmU#YD1}$>uD%VC1lTT!O5qDn_O$TZW8L3TdkjY*n)qN2wS}pp2?z7LV$U$K%CF z#=t167slpu3kw!nG2;eaoG&(~G&MbX_Wa0#qU&cy&Ww%?6rgA^8BFj=5~VN!bxjBF7Ay&nl*wH?V5+meE+_%-sAkN?FBiQV>32yfWDp#v-wx z&6t+$wlqsewmXkC3z7F*`qyoblFd?1+cQt4jEv3Y6`Y~<+nkxzG8u%ii<)io^R!CC zagqw8QuO&_K3iDG>3TAwq>;_%twsUf!;U0%jp_-el}jGxw$E_vZY0eGq~N5nknEuV zwQvOH9k8=u2h9~t*Ue<3d8Cc$B!9L>#h5Po0H|qYU|V!|Ht)!xH(&%t+ox+0)u}6 zC@4(bFI9WqsE*H6RecYD0!%P8Of%P=kYMJVFvldFkcT&ze*p+6%nKF@>k6~WdpCV$ z->pEMPP}C=#Y7ATB4nAp0N_I}JdVWPq0oWpr*{41j$HAxKl|NRTDX|U=@3RrTGuRm z%IUM1V*V@9BnF24AEb{{sdll2HHv diff --git a/webpages/__pycache__/navigation.cpython-312.pyc b/webpages/__pycache__/navigation.cpython-312.pyc index b38aef41c2876a4544d0ca4c4d43e12c77c5d6b5..dc8e3cec90219845792079950e6271764e018f50 100644 GIT binary patch delta 718 zcmYjO%}*0S6rbsK*=@TmUFhewv_+{zv6rFh_@L@s)>46=7Lz@pBrM@5_&L)3c4As|LO82u2yd6RO7COMJ62$dyyik*34IjWUjQ}TUIOTI|^xvxT6i$;@e3T!AAv{2ZzaZz-IsgCw delta 690 zcmaiwJ#5oJ7>4g^J9ZuC$4%nY&M%bE29!wsp|ql{R4TPn$%H^m3~A3w4QUK^(!mmH zMmkmDhDKtkm{KLG0xRqcEFC#=6%GE5~9dH`|xoy2Js=4z(>&ID!`VnNxg$9%8x`xrAANyS7Dk8qEMfu z0f*6OUzd^6uMre=Y9NN+y`d#Hv`ksoi>Ae{>sl%K)U^0lszI_c zd4xJF(sneY;yKsnc$|#ojArT(fGYSQMyl&Shu!! zmphT~wzIadOb`oPigp2yA6M9gUAe8;!G+Uc_#juy9sZxXZdVRUJ>U|5Nu?8cltrnKdt)W;(-W*--P_MXhW|_&t}n mArp_`eA5v)BPTFo}yS2$Ea9G*^|3!)lVI5fYrGO=n+HsJ}Dhbfw%cZEX{i<)ITN-|m>7=X%u ZeOF>+wfMjQBz}Eo0SbUcCqLn70svbDEnENq delta 139 zcmaDP^-PNQG%qg~0}!lBHB3Lbk++apOf)z(FEhVLA-JS8B{i=^Au~_G*)w?a1m*?~ zM!m^`JYt+WS2%RPvof*jO*Y^Ome;$(!PU>%$vHvq3Wp+y&C|(qK@_285s#9L-UkMt a@?YPT7+EboFaU{P-&ueHVA07Bc$xrZc`3C3 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 4f67dc5c7b440a3a1db5f201ce79dfffe8d02625..f3b25c25b9e4546ee877368a21c03e635d2f2656 100644 GIT binary patch delta 64 zcmbOwK2@CiG%qg~0}!0mY23)I#Kg!xS&zw(QDAZclQz3x3RenuiV%=3K6xdR0;BNe SQ%vlvlV#Z4H_LL=G6Mk9Dh{Ur delta 66 zcmbO#K1-bYG%qg~0}%WXjorwt#Kb5tS&zw(QE+kslQz3h3RenuiZGBZK6xdR0;9<0 UQ%vlvteIaJ85lNSWIw_T0Q4IW2><{9 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(""" +
+

BuffTeks Student Organization

+

Building Skills, Crafting Code, Bridging Communities

+
+ """, 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 +
+ Dr. Jeffry Babb
+ BuffTeks Founder
+
+
+
+ Dr. Carl Zhang +
+ Dr. Carl Zhang
+ czhang@wtamu.edu +
+
+
+ Mr. Kareem Dana +
+ Mr. Kareem Dana
+ kdana@wtamu.edu +
+
+
+
+
+ """, 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 0000000000000000000000000000000000000000..ee1f2c4b4d1de01c5c89cd57ae65f7e16eea8e6b GIT binary patch literal 4906 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..224f64aabe3d0af53f085db31d3b3c494a2791d4 GIT binary patch literal 7136 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 U?DRcPw`Z8M1-NoFj?lAKy}Vk>ru6?aKdA$NJZ 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 V{{&H_=ZV`uHa)fPC0#1#{{^ti3N`=$ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..79cf90f16f04f89c33730924ce532180335f0b4f GIT binary patch literal 22680 zcmcIsU2GiJb*7}HOv|DyrHX%K*}f7JlUy&CTvAG$wMa+Sk4;;Wl~{?B*oHK_GfR#& zJF}jd70Hd=)N|2g;Ed-==B$s=R6V7L$9%;@Cd1cyb?D z952P9jK3#JM;^EI>rejIN=KIQ_c!)M*-8KF`=IQpQY^}J?CH{xWjuJMbYvM1o-G|& zrU$1=N0xExxqWf#wEz4FZk6HtLMawyJb1BmWEo{*&m+&2jx6KB*&!b6Zevpp+}&;W z8~!&(aI1{6mmIhjgED-lhw$AUiuGE5qPh&d5hm_U#cEl)-() zfom}+)AsK;BX`H(h~L}?2Cq7BcgNtuzI1HaUm1Z#nIv8-#iER3-z^_(tl$F7G?Z>vlNRmd{;|Hmf`!>5Wc%p z_x8RqMdYm{X9rTPXg>>R$aUoh%EQgTx^8-y1;^7b{De&YaXwdC(J$YPBf{LA@B(tyaC*i?#+pFAe%1hiT9a;ykNX ziOSH&=diQI%VAO*tfF>WnHJ)9LatuE;9}{5Ax$=`03EeV82E%4FA~NEN#-MVT*Ax1 z3PZG_VZ_Qyr7QbEFG{wUILie=!p2tv*^7Mv>P#b3x`Sjv!`8xkfe1ExK|2TX5HyAb z*%1G4t%sSwf1b#Q^sqyODy5J*;1i8A1_3P|w9eLe9nZ&Z6I$s-P>i6LiLSRLTHsuK zFNy8}94HH72BWAUsZ3`|Qk!AE#c+tu9gG{Cs?UdAZ!O5?FC_%yrW8WG7q1Pf&3q$) z@`q`bD}xZ|C*^O7Hv=fTYW>lfLH92(el2zJ=ZYQL^RRalhLN(rkOYv9QqF9DFTeC(@XM9m4dJ zH1cN;ZXaF`!nO5$`>0&_cI35!$onNq1ZFYwVO65&wGwC)6&RYd;M*ms~ z26zK_PFxgO@Nqwg+X3k&Nbxi2j7Ue>9}z3>Ox+RLR=1Tz!b{n9*r18i(ra3~ToIR& zuI#0u{S4{~mO~}(!SKm?BDf&Z?o1@9h?5+?2z-DTrCItxoxBz_7}LYGI7tDawWYO@ z?9fkWp71&ySfZ5mX3*}lBL@`(N2y@lV#7YO>)n-ZjCd}(fDJ$B`CbdkM3DQj4>{-f z$W0w!m;ju0*yts+siV`+`#irBq{KkS59}0SnVOwBEA@=xNl_|yKrz-dqucZ$k>=BxqD>j_@Z73f8~ zCi}Hkl)#$6=pFUp8I;7jm7ttFkMUYX-)+e0LYJZLk=4$?zXE*KdY<8>(XdV)+^ zd16;+*VvO~fzpdW`k1Uq;xQKEy^7Swe6xnR*d$e#u5M=5$cQzcyq_OJS)nGm0Aa8( zaF6+%8CX)}jm4#NvqDV41i)*?lslyC0V!Am4K!+K;#sfN_JhvZ(UZYr;_0*;I6X9f zsopr(IRBl+Z=GKn4WbhzFx_}rSB3!XH!KBPq~@<9R){=A1SSUq2STC1@>#N1ngESVlZ^~IvX1!T zY4-qaV{Jl&?MHb?Aw}kH5DzJ$w1PR#q5|t;QaA}R#}4I)WRrQ)BOK^sE6MYuTcZ^y zwIPlN9)ci?Qu^9F^rC1>!X7-^%!!buxSWXV9`ZlqQwP`Sdq8Y#BNIqqPs(qaA?zcO zW`vQKN(2%pVH9|245x`rx!uv8hp7?%>_c_9;UN9nD(rqwJ>Q3#vzdKfjNhSX$Knx=-qw`%wjLB zV9hC51wd(#L8}N>rYDg7wWg)wxU5ltuS+e2yvcMF>?*-70ETYSzU2{EjsVu)1F$w2 zFTrUzT>;q_LYLKJdDD8gNlZbXsND^iG?Ic($aLHlJNr-2i4U>WRPKh_MI@!by z1W{LPC4E7uL54Ft+HChbojOGul_k_jau$*CqOpqYz zy8(9eG(zTeI`Lu;6?hr4zl2sR#|f0uRGF%4q6;d(frQ2Yn*az9qGTIn4vPEm_cgI3 zRR-SH&C!5B2$F~jpbAW|kGN}g4_yjG1X~6pFKYKA8OUssIe5-&O0^+SAdLdvm7Z>P zFuzH;d=G&I^&5&eik`t=qJF}8J#8a(cW^IAw|r#9oYg}N?IcaHTbSQS6QT=Mjr9Oo ztSm9eS#@!xzE*EiV1x5n!^VIF1yzj2D|#=B4h0G6v^2jo=)q-CY6oqRdvFQQ;`o|3 z<{C5PA!WgeMT$v^VmG{UtHqogCd@I=PsF=&OZJJl9v}|~S;e1Ej9Pv&r+&-_-Jsj; zP_ZJRd?MaZ9JoasFh34daX}BgMx~!A{gM92Q?LOr90-oVyz=WsdHumL(na*-+p+ufj?2K%rK;ezxi+NzNWn||1dPB^gv z<q%=`LNIHi-oe4?q_#;#E>r6Lp}P};ho?R4t+XHD7h4^EU-hC)euvw| z_g0cb)ICEVptG~mo#k6SuG*+t0u%|`e`{9N>LqEs2n=M-)#`g>#MLV23RI0lT@DE~ z6;erPSQO^X$_szS3ppOuiFH~qK&e-(xP;kl4{<(+EQln-5ws(EP@NP#Or7Zh5F7B` zqe?D0PpqF6jgiHzN|c~ibY`I6(*m`*AK7kxJQEkt{;28lUQ%^PZ0#p=xtn#on7 zK1fL;x07z^3HVz|84L`HIMQIVjyGXIqp{aa_e8!mLNef#Q5R5xPbx$Q??OC zk=%vo6lI7`Y%vg@QT=n>uS)!!D6fcLM=3ZXhYmks3I*0X8un2LZ%2I&Tww>_I&%T^ zuOfwe#rprLmjM5lLHsSaH!6kuVaUlyn zLWx3Fg$tS>W=Or}S(9c}xs)V;V(;#(S(Rn30&F0t>`!i~7fWQR#@eI zt=+vh%!rF?0Y#2;ubLJBrpA@!#w$g}s(sop1#tgnu3cf#w80KgmVOhp zvnlB&DF~5r&aH^6C`VY2N`iLqG0ai1$mwRaw7OCgq=Bq;Slrm8GjGsU4g}d1$(r0Y zC!S)aTE)=>PPDQfYJ~_Z(4uNJ_gWNTWJS3Ztc5WrRq)=jW;jdk7b!b7UGVIr>ar3V z!8b)&9a}j*9Wc?7)mOXWUl!!2JqtYcA_9>RI>tj-@*M241Vv-qMk07LDE zqp(>=Wtw_~F$|=A2s;`3V6(k5WnHr(s8Frh z(D6T|ta39YHX%7(Mt}uxQhL`*GS16uVi2SWZxQ2Q4M7Jvxxs$$_vYg!0;zQr1Z8Ex z4kQee8z?|8SRzeYUfyLEVN#d@zX=brdBH9`ypYD(nKpQ`V8mg^yH;Ylxc7pHF*j_y z1fPCnevZAK6RV|5gHi*mLTwLdI0H~6{yfU`6e9(LOXb|nT4B8 z@wzIoT%$W~2x0H=F(oPk-V!b+8s_O?d=(DERC=L`APxa-;)40p8ms)}%!e>|@)-XZ_F5mLzmHzzD?I8jbDEFXaAkk6F*u(;CTl@~AI z7Jv-F>R=sbL&Z`UKv=6c*s7k45)Dy=0e}fQcpi+OcX0!=xrzjvE7X_P^ zq4X&WGguX=n65Dv3>BL*skX2fH(#VOUysuiuZ!$`0$v70n3DjuT3#mEV6+SR}L7T8L5@W)1xL_KjgUKe@ z8ZNk8OOjsZ-E?;n+*HU4=(qzx9tY|A;vu7L_V#=SOx|wfc@%8vNQsRacR+dwUEmQa zessSJ--%%qU-6rY0ji0`OBAbf03J_6so!*(4_D2#e+ZIwjPLrJ0}Apem|cM4oZh0# zF-=+)Nllv1@!6VLMBI z_%Q$-5JwRmKqfKj(S6+WLmn5&fP&i`eB;u6Ah459};#syOU!Jw}0 zjY1=-WQ`3MlrZ+_k%G8JU%A6BL*4S&+CZSusPbd%vUq=5o)6dfaJV*o$;;Qbrfbue zJ#5IQKQwcQ2AYTMEdS>DMG};3F~|yca9q0y0FVN*2lgbOYh=4J?jpB)SD)LMb#dM+ zMTEHCqD#FbWIMs#B_RFAogmzq%k))bd>uXw{9AVeYEMA$%0O#774e{n^cVmqX~QHkYqKscB92k?;jc1=sEf%94xo#ojjaSkZF~*QVQ~7mxlPxVk>)~Y z`+RMg_L>qZrVSIq=y`wa2JZihmW00XHH}82wz#-hTUuJGojZ4~cK-Z%>tVX#rX5(* zZi%-(i}j_Y5ADWETj~a`ut^GyDM%lrK^2Zjq+w_YaPzLnP{ICF4-IRe!1>GKAj*(UvNZe#tZv}$GA0Ool=|f44NuTH-1QCVRig!!Yh}QsU zpRtUwAnU4c`Z@xOQ)@06aV1^@Sa*w>^H2k8_XeAVp*i{(?E9+{NXPBfpn!X{W)x z7ZCw;M_I(>E1Y~4mGud8?*>~GuOz5||uG(>h$quf{0?TvmM;=z`f;d@N_GSceh zS!wOw?-yejj)^TUkWJ{%ky^+_jFh3$tSmGZasQ{LgZwTGv#Bkv&;e_0%FLk93nKUQ##SA!O1*?tBBKtm&D{3=xDfXrmsmTl5 z2R71zYrG^mLWkx$WnkINY|*9>pF$s-%^>S1EH1t2Mi5uW5J`?m$9aXkz zsFlOucoP>3FxtF>D&9j{EnW|m7uodt7mgY9=1HCyAOMh-=w5t89*S3~@?8uHvUCyJ zqakQpPh>fhW14<9HMFbZEQUi2dyE=i2%{nrwh-*fZ+b5idl!fstmm4Vs8}@B}O*?FaDqg}jJyY@=$J8pKSw)#eZ+Nn5FL}1`m4frV$$#mrrvt-`dEJs%4HR96_ zcA8bBJUc@fFz1YP#`M$Z(^uJM0l_4b+>z_J%AlO$fqG5s{h7NP!TTU=Yx$i%+za0z zprS}CP3Vaa4p#1k!3Hg$0t4K4w}*tEUID7sn_N1l+4T1h=#dbPisQ9D{&s?M#m*46 z%J)B~@z*5yHC2Mu@eWg-Ny=PQ0fe^T=})d+JmX-&LsfSbnn0UEa{BMTn?!VNJ&iw{ zaZ}e!H*@M5#Pd96zKzE*I6+WJ>>O&o=I@%!(rf4(YKj-B@+s6RP9*!-kcjRUDk2@d zKSn44eby}+s!(-F`B|t*=wo_kE;%bNva35(_+uLgkLpbtc@ae}d`GJv;wcxheLm9F zSKUd?5;=LQv5qaHKUYm`#1d`&6LM^1s ziN7PZrnt}^;8b|+yBoRJ?bidpzarjA#rxmKB~MgJQ-{qopubV~28}x1{zCZkKYY`qw;oB2kFTLw08}ary|5c9d3PttpgV?Vh!k+@%9^b%2+~9Ap2UkN(8R2XcRd%`;m< zcDvZ>s!F}LwS7_#cRSKkSegEOQ1(wa(f{qSf2kb)-8Vmb^Yh91`;~=%$CuU5RzE*h zyL(YLywP7K0G-#{+Jv*d%seBc$mKO@aFx>s|1LKkByzUeDLAe z*!Y!$)bz$@Z+tO!Xma9~(Yza<-T2e#&*v{as9e?yjh#4g^x@bE zEc6W;!86AvkA87a-ls98L3gA5<<%eliIsY3@nPt(v(fu&{TL#VNX~gpwaY2K8>GS%H2bFgW$lpuuS6(m>i)1N?1>Jp|kT*u6b@$=e zIOsX57lFcGd{BAapmB&M9m0qSLvc|%T|QfGu|k}?Upa5^dyMFL4D=i~fSv~H>cqPF zr@^`fY6q7;KmNn}mG@{K6Y;a(d{9|&L|_bwQD!Qh1e_NL&Wle1j&MlP^fNl*e&vio z=P1#26m(hrIRY1Ol3c*okBpywc*>wTiS|>}4r@H^SjsW9pQd)`n`&2V*BpdNelZ41 z`HCzheFiRV_VD)Oh{S={jlw*A5-r;k+IO=5O}~yd_JHZr^ob^=&*Gn7O)1%@QLq6E2oM_xU>803*c@BfhyDq@w+9yy+G6XRnoDm6>{Cwt-s}(34pEelxU)NN z-n{pH-+QzC%k1owh0kgGuroJfS^uV+>@$%Lo}I(sf#q0E!LzmsxK4NzesQZ*pl3y| z>{qrbW?b+l{pwZ?wvQ5pHXnGIG{D*>mi48*?e+ zyNLg1bNGH>=|VQSn?;j7z||={Oy>)umZcGsbm&ZswXBS>ERMCTjjwr@?^;T&_iLYli!)q4n3trC*=d+P>eC+-#n;%|pmYju0Cx&?q ztj5VJ2X9^$O38q|x5CyU!L?v{87bxlY-6CiGB`N3A_K9)HoIIiSB=>4-=JWoSo*oZ z`Y1wXQx&p0_mpINf$Rs2E5@1Ton)#(J~+GKsvh@PB*I825ooSmL?YWvcLi%l;(AX6 z_JAIcR}&6r9ruO^m=^oG$*#iAmr-u4y#d^s5O^309+@@djqI0=Uynx}5iwV@mrRC{ zH`%($Qnod=>M7wQ@%QD((FYchc5P4KmRz=^+*8dQ)Ppn4cU+%$gn9=iAnU!iDOLk=GYmTX z_oEbC9fYpUJ#PSCqig_i+Yk)@NQ3XWK?fG0D{PU6J4}dX3Am-DoQS0*vJ)J+(EbVh zG@|$caTj3mDB?i}fI&Jr!EG7&9Pno_@KhkG%R{lu`mXMpy?h{&@vc+O6Ot$zc%*;~ z&SWP8%@kO8IgW(2(LfNABYgtgC50)oW9rVJI1+={w?&xgkh8Yvr-AglE|G{tGe1d| z6q60{LQ^tI)hYb2%auFI;m=ldU#_rE6{V8X$2PdV2WClD9TA9#3UD}USmz69MY$N( z2?cN|_PHN=Fvg$jdCY^_1J;#&as|o403kt}EKIjR!bvfcg)M0{Hq%_~C1eS%1`HB! zPUV|j$X4V&sBCTNdBV;xiiC%tQS&&AS=)s|S)KHUN4|7=xNjh0v<*aRb5l+fKv#HS z!n_KF?Y3Rpgn|n2+&xq^*Ilq8BO_CzkffA=v5_&s*ok-uR+LcyQciX!ie%K`0kmOs z*p{9nB6#)_(WCT{s|&&qhmZON{v`ZJ5N?J~QrcX*h%S*uyBZ)YD1i2x@9@LvpB;II^?ezfOm7c$Wstw_ur=0*Wgr3x%+iGSl zmgVg5rO~=iqYZmH5eRiWxJHVU5#6=qzG;C0%Fng;*oCBXEmJQv8p;)zG>)W;8x5k` zfEN6?f!P4whW+W>r}n(I{fzI=UNIy29;rb^J;T)O8|sdS?g4Afve2ay>Ud3F19; zmU4(fiLyYmiHow`=6}tYa{W+7=vx}C*!SoV!l|GQeC-H5YP9x^IV`wR&31puU*mvDjG2*?l8>N8rOZ z047_z;3l^jX0SRmsv7`TfBNpb%cy(;arJ!~zYOH+Sp)gmMl;i5opNa8-rH^m=h{A7 zrF5IB*P`rIeQ3bkj|Aa5b{;-H zSX^5AMD`*aoom$h(s!0mlAe4a44X?!SD}R&QpHZt^IK>@jrn*&>A0+2?FkYSM_Y=k zK9BYsG>o`xN-eJCti_dti-_Lq<7x&;Ttu{SHA_COBnihQN+7Nz@y3&md&8v-YA{Uq zDp8Yel;aX@5!Z5r;v%w(C-{CmtxQ&swIdY$bTUAEyS28m{i#9@+Xr1#^7e;9?P(cq z|DxA!Uln_5Q?zilitWB=h4iAb9hw^5-$`Cr)Xs}f^Nj&+JfGR-Pop=n@g)A$Yp9#s z){|1@^N$ZdemwjBQRxG^SwCEVJXb#|HR$Hj;iae6#Qe;2Yoa{+ylRyv6XUm!O3m-i zS7_d>Uq32c%;!tAzJŊds1=Bv-GO1buY&YGFKcmD4A$EVJHHT{#@AAU1g`+RUX zxc}PGempNfC(94%b=$HL~{7B(OMYWu5&U*D-cdG*AD<=-y`3`z3ScS{S#jtdy-DQ@x|QJ>fG^}+n1i7p(rjMUi|Db zMR@t}^5gjrj!Ng~=A*-p?p*rP`FrWd&w!BCm;?|*{u67rHgeC literal 0 HcmV?d00001 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("""