diff --git a/app.py b/app.py index 1cb4ef4f8..aa23bcdd8 100644 --- a/app.py +++ b/app.py @@ -39,11 +39,16 @@ elif page_label == "Lesson2": pg.pythonx_lesson2() elif page_label == "Lesson3": pg.pythonx_lesson3() - +elif page_label == "Lesson4": + pg.pythonx_lesson4() + # block of CIS Tech Challenge Event elif page_label == "CIS Tech Challenge": pg.cis_tech_challenge_homepage() +# block of CoreTeks +elif page_label == "CoreTeks": + pg.coreteks_homepage() # block for testing page elif page_label == "Testing": pg.testing() diff --git a/files/student_locations.db b/files/student_locations.db new file mode 100644 index 000000000..83a9117a6 Binary files /dev/null and b/files/student_locations.db differ diff --git a/images/11-05-2024-Streamlit.jpg b/images/11-05-2024-Streamlit.jpg new file mode 100644 index 000000000..e5f573eab Binary files /dev/null and b/images/11-05-2024-Streamlit.jpg differ diff --git a/images/11-19-2024-CaddyAndLinux.jpg b/images/11-19-2024-CaddyAndLinux.jpg new file mode 100644 index 000000000..d7adf764f Binary files /dev/null and b/images/11-19-2024-CaddyAndLinux.jpg differ diff --git a/images/CoreTeksPicture.png b/images/CoreTeksPicture.png new file mode 100644 index 000000000..1d69c27a7 Binary files /dev/null and b/images/CoreTeksPicture.png differ diff --git a/webpages/SendEmail.py b/webpages/SendEmail.py new file mode 100644 index 000000000..035cc6948 --- /dev/null +++ b/webpages/SendEmail.py @@ -0,0 +1,115 @@ +import smtplib +from email.mime.text import MIMEText +from email.mime.multipart import MIMEMultipart + + +# Function to send an email notification +def send_email(sender_email, password, to_email, subject): + # Create a MIMEMultipart email object to support multiple parts (e.g., HTML content) + message = MIMEMultipart('alternative') + message['Subject'] = subject # Set the email subject + message['From'] = sender_email # Set the sender's email address + message['To'] = to_email # Set the recipient's email address + + # HTML content for the email body + html_content = """ + + + Welcome to BuffTeks! + + + +
+
+ Welcome to BuffTeks! +
+
+

Dear New Member,

+ +

Thank you for your interest in the BuffTeks student organization! We're excited to have you with us. To further enrich your experience and connect with fellow members, we have set up a Discord server for BuffTeks. This platform will allow you to engage in discussions, seek help, share insights, and participate in various club activities.

+ +

Please join the BuffTeks Discord via the invitation link: https://discord.gg/dU8vKfAjxv

+ +

Once you're in, take a moment to review our server rules and introduce yourself in the "Introductions" channel. If you have any questions, don't hesitate to reach out.

+ +

We look forward to seeing you there!

+ +

Sincerely,

+ +

BuffTeks Faculty Advisor

+

Cheng (Carl) Zhang, Ph.D.
+ Paul Engler Professor of Business Innovation
+ Assistant Professor of Computer Information Systems
+ Paul and Virginia Engler College of Business
+ West Texas A&M University, Canyon, TX
+ Email: czhang@wtamu.edu
+

+
+ +
+ + + """ + # Convert the HTML content to a MIMEText object for HTML format + html_part = MIMEText(html_content, 'html') + + # Attach the HTML part to the MIMEMultipart message + message.attach(html_part) + + try: + # Connect to the SMTP server and send the email + with smtplib.SMTP('mail.privateemail.com', 587) as server: + 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 7ebc3b6f7..4dc3667e9 100644 --- a/webpages/__init__.py +++ b/webpages/__init__.py @@ -9,5 +9,8 @@ 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 .outstanding_members import outstanding_members -from .cis_tech_challenge_pages.cis_tech_challenge_homepage import cis_tech_challenge_homepage \ No newline at end of file +from .cis_tech_challenge_pages.cis_tech_challenge_homepage import cis_tech_challenge_homepage +from .coreteks_pages.coreteks_homepage import coreteks_homepage +from .SendEmail import send_email \ No newline at end of file diff --git a/webpages/__pycache__/SendEmail.cpython-312.pyc b/webpages/__pycache__/SendEmail.cpython-312.pyc new file mode 100644 index 000000000..85d777c1d Binary files /dev/null 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 a032099f1..348dc8202 100644 Binary files a/webpages/__pycache__/__init__.cpython-312.pyc and b/webpages/__pycache__/__init__.cpython-312.pyc differ diff --git a/webpages/__pycache__/classroom.cpython-312.pyc b/webpages/__pycache__/classroom.cpython-312.pyc index 35c3e5d6a..615e17656 100644 Binary files a/webpages/__pycache__/classroom.cpython-312.pyc and b/webpages/__pycache__/classroom.cpython-312.pyc differ diff --git a/webpages/__pycache__/code_editor.cpython-312.pyc b/webpages/__pycache__/code_editor.cpython-312.pyc index 79529b445..bea0fa418 100644 Binary files a/webpages/__pycache__/code_editor.cpython-312.pyc and b/webpages/__pycache__/code_editor.cpython-312.pyc differ diff --git a/webpages/__pycache__/event.cpython-312.pyc b/webpages/__pycache__/event.cpython-312.pyc index f81ff1487..69c99eefd 100644 Binary files a/webpages/__pycache__/event.cpython-312.pyc and b/webpages/__pycache__/event.cpython-312.pyc differ diff --git a/webpages/__pycache__/home.cpython-312.pyc b/webpages/__pycache__/home.cpython-312.pyc index 36d545006..72181dcae 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 7ff5acaae..b82ec2fd0 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 d8c8abff3..b38aef41c 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 f41964334..a61b9a970 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/__pycache__/project.cpython-312.pyc b/webpages/__pycache__/project.cpython-312.pyc index a42387e6a..557042000 100644 Binary files a/webpages/__pycache__/project.cpython-312.pyc and b/webpages/__pycache__/project.cpython-312.pyc differ diff --git a/webpages/__pycache__/reference.cpython-312.pyc b/webpages/__pycache__/reference.cpython-312.pyc index d34990380..65bfa122e 100644 Binary files a/webpages/__pycache__/reference.cpython-312.pyc and b/webpages/__pycache__/reference.cpython-312.pyc differ diff --git a/webpages/__pycache__/testing.cpython-312.pyc b/webpages/__pycache__/testing.cpython-312.pyc index 07b9aadbd..ab1531f6c 100644 Binary files a/webpages/__pycache__/testing.cpython-312.pyc and b/webpages/__pycache__/testing.cpython-312.pyc differ diff --git a/webpages/cis_tech_challenge_pages/__pycache__/cis_tech_challenge_homepage.cpython-312.pyc b/webpages/cis_tech_challenge_pages/__pycache__/cis_tech_challenge_homepage.cpython-312.pyc index b5028bc2d..88fc08349 100644 Binary files a/webpages/cis_tech_challenge_pages/__pycache__/cis_tech_challenge_homepage.cpython-312.pyc and b/webpages/cis_tech_challenge_pages/__pycache__/cis_tech_challenge_homepage.cpython-312.pyc differ diff --git a/webpages/classroom.py b/webpages/classroom.py index 7b52b0691..0c7f1f82e 100644 --- a/webpages/classroom.py +++ b/webpages/classroom.py @@ -2,6 +2,12 @@ import streamlit as st def classroom(): st.title("BuffTeks Classroom") - st.divider() + + st.divider() + st.title("PythonX") - st.image("./images/PythonXPicture.png") + st.image("./images/PythonXPicture.png", caption = "Learn coding in Python") + + st.divider() + st.title("CoreTeks") + st.image("./images/CoreTeksPicture.png", caption="Sharing and learning new tech skills") \ No newline at end of file diff --git a/webpages/coreteks_pages/__pycache__/coreteks_homepage.cpython-312.pyc b/webpages/coreteks_pages/__pycache__/coreteks_homepage.cpython-312.pyc new file mode 100644 index 000000000..4f67dc5c7 Binary files /dev/null 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 new file mode 100644 index 000000000..03d980e8d --- /dev/null +++ b/webpages/coreteks_pages/coreteks_homepage.py @@ -0,0 +1,35 @@ +import streamlit as st + +def coreteks_homepage(): + # load pythonx logo + st.image("./images/CoreTeksPicture.png") + + st.header(" :clap: Welcome to CoreTeks") + + st.markdown( + """ + CoreTeks is an engaging and dynamic course designed to explore the ever-evolving world of IT technologies. Our mission is to connect learners with industry professionals and passionate individuals who are eager to share their expertise. + + Through a series of guest speaker sessions, CoreTeks introduces members to popular and cutting-edge IT tools, techniques, and innovations. Whether you're a seasoned expert or someone with unique IT skills to share, we welcome you to inspire and educate our community. + + Join us at CoreTeks, where knowledge meets collaboration, and technology shapes the future! + """ + ) + + st.divider() + + st.header(" :paperclip: CoreTeks Presentations") + # for streamlit talk + st.subheader(" :one: Introduction to Streamlit") + st.image("./images/11-05-2024-Streamlit.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/EZBXalcLWaxHquMkZxOWXz8BV2yBo_A1OURZin0ZM0XliQ?nav=eyJyZWZlcnJhbEluZm8iOnsicmVmZXJyYWxBcHAiOiJPbmVEcml2ZUZvckJ1c2luZXNzIiwicmVmZXJyYWxBcHBQbGF0Zm9ybSI6IldlYiIsInJlZmVycmFsTW9kZSI6InZpZXciLCJyZWZlcnJhbFZpZXciOiJNeUZpbGVzTGlua0NvcHkifX0&e=SJD0A4", ) + st.divider() + + # for linux talk + st.subheader(" :two: Linux Security and Web Server Setup") + st.image("./images/11-19-2024-CaddyAndLinux.jpg", width= 500) + 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/join_us.py b/webpages/join_us.py index fe9f46d80..22d441fa7 100644 --- a/webpages/join_us.py +++ b/webpages/join_us.py @@ -1,4 +1,51 @@ import streamlit as st +from .SendEmail import send_email +import time + def join_us(): - st.html("https://wtamuuw.az1.qualtrics.com/jfe/form/SV_2boQtKLCptO33HE") \ No newline at end of file + 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**") + major_list = ["Accounting", "Computer Infromation Systems", "Economics", "Finance", "General Business", "Management", "Marketing", "Other"] + stu_major = st.radio("**Major/Field of Study**",major_list) + if stu_major == "Other": + other_major = st.text_input("Please specify the 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) + + 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") + if "Other" in skills_selection: + other_skill = st.text_input("**Please specify the other skills**") + skills_selection.remove("Other") + skills_selection.append(f"Other ({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 diff --git a/webpages/navigation.py b/webpages/navigation.py index 6db03b6bd..ed6086d71 100644 --- a/webpages/navigation.py +++ b/webpages/navigation.py @@ -25,13 +25,16 @@ def navigation_bar(): sac.MenuItem('Lesson1', icon='1-square'), sac.MenuItem('Lesson2', icon='2-square'), sac.MenuItem('Lesson3', icon='3-square'), - ]), - ]), - sac.MenuItem("Testing", icon='fingerprint'), - sac.MenuItem(type='divider'), - sac.MenuItem('Link', type='group', children=[ - sac.MenuItem('Join Us', icon='person-plus', href='https://wtamuuw.az1.qualtrics.com/jfe/form/SV_2boQtKLCptO33HE'), + sac.MenuItem('Lesson4', icon='4-square'), + ]), + sac.MenuItem('CoreTeks', icon='bi bi-tools'), ]), + sac.MenuItem("Join Us", icon='person-add'), + # sac.MenuItem("Testing", icon='fingerprint'), + # sac.MenuItem(type='divider'), + # sac.MenuItem('Link', type='group', children=[ + # sac.MenuItem('Join Us', icon='person-plus', href='https://wtamuuw.az1.qualtrics.com/jfe/form/SV_2boQtKLCptO33HE'), + # ]), sac.MenuItem(type='divider'), sac.MenuItem("Reference", icon='paperclip'), diff --git a/webpages/pythonx_lessons_pages/__pycache__/pythonx_homepage.cpython-312.pyc b/webpages/pythonx_lessons_pages/__pycache__/pythonx_homepage.cpython-312.pyc index 3066e0bf8..c2480d346 100644 Binary files a/webpages/pythonx_lessons_pages/__pycache__/pythonx_homepage.cpython-312.pyc and b/webpages/pythonx_lessons_pages/__pycache__/pythonx_homepage.cpython-312.pyc differ diff --git a/webpages/pythonx_lessons_pages/__pycache__/pythonx_lesson1.cpython-312.pyc b/webpages/pythonx_lessons_pages/__pycache__/pythonx_lesson1.cpython-312.pyc index 642725e99..50d22bffc 100644 Binary files a/webpages/pythonx_lessons_pages/__pycache__/pythonx_lesson1.cpython-312.pyc and b/webpages/pythonx_lessons_pages/__pycache__/pythonx_lesson1.cpython-312.pyc differ diff --git a/webpages/pythonx_lessons_pages/__pycache__/pythonx_lesson2.cpython-312.pyc b/webpages/pythonx_lessons_pages/__pycache__/pythonx_lesson2.cpython-312.pyc index b0a485091..260e5d2ef 100644 Binary files a/webpages/pythonx_lessons_pages/__pycache__/pythonx_lesson2.cpython-312.pyc and b/webpages/pythonx_lessons_pages/__pycache__/pythonx_lesson2.cpython-312.pyc differ diff --git a/webpages/pythonx_lessons_pages/__pycache__/pythonx_lesson3.cpython-312.pyc b/webpages/pythonx_lessons_pages/__pycache__/pythonx_lesson3.cpython-312.pyc index 4957797e1..a8fe8074a 100644 Binary files a/webpages/pythonx_lessons_pages/__pycache__/pythonx_lesson3.cpython-312.pyc and b/webpages/pythonx_lessons_pages/__pycache__/pythonx_lesson3.cpython-312.pyc differ diff --git a/webpages/pythonx_lessons_pages/__pycache__/pythonx_lesson4.cpython-312.pyc b/webpages/pythonx_lessons_pages/__pycache__/pythonx_lesson4.cpython-312.pyc new file mode 100644 index 000000000..1205b82bf Binary files /dev/null and b/webpages/pythonx_lessons_pages/__pycache__/pythonx_lesson4.cpython-312.pyc differ diff --git a/webpages/pythonx_lessons_pages/pythonx_lesson4.py b/webpages/pythonx_lessons_pages/pythonx_lesson4.py new file mode 100644 index 000000000..48240dd02 --- /dev/null +++ b/webpages/pythonx_lessons_pages/pythonx_lesson4.py @@ -0,0 +1,123 @@ + +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 + + +def pythonx_lesson4(): + # Initialize the database + conn = sqlite3.connect('./files/student_locations.db') + c = conn.cursor() + c.execute('''CREATE TABLE IF NOT EXISTS students + (id INTEGER PRIMARY KEY AUTOINCREMENT, + city TEXT, + state TEXT, + country TEXT, + latitude REAL, + longitude REAL, + timestamp DATETIME)''') + conn.commit() + + + st.title('Where are Members From') + + # 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: + lat, lon, city, state, country = get_location(input_city) + if lat and lon: + save_student(conn, city, state, country, lat, lon) + st.success(f"Location saved: {city}, {country}") + else: + st.error("Unable to find the location. Please try a different city name.") + + # Display all students on a map + st.subheader('All Student Locations') + df = get_all_students(conn) + if not df.empty: + m = create_map(df) + folium_static(m) + else: + st.write("No student data available yet.") + + # Display student statistics + st.subheader('Student Statistics') + if not df.empty: + st.write(f"Total students: {len(df)}") + st.write(f"Total cities: {df['city'].nunique()}") + st.write(f"Total countries: {df['country'].nunique()}") + st.write("Top 5 cities:") + st.write(df['city'].value_counts().head()) + st.write("Top 5 countries:") + st.write(df['country'].value_counts().head()) + else: + st.write("No student data available yet.") + + # Close the database connection + conn.close() + + + +def get_location(city): + geolocator = Nominatim(user_agent="student_location_app") + try: + location = geolocator.geocode(city, language="en") + st.write(location) + if location: + location_str = str(location).split(",") + state, country = location_str[2], location_str[-1] + return location.latitude, location.longitude, location.raw.get('name'), state, country + else: + return None, None, None, None, None + except Exception as e: + st.write(e) + return None, None, None, None, None + + +def save_student(conn, city, state, country, lat, lon): + c = conn.cursor() + timestamp = datetime.now() + c.execute("INSERT INTO students (city, state, country, latitude, longitude, timestamp) VALUES (?, ?, ?, ?, ?, ?)", + (city, state, country, lat, lon, timestamp)) + conn.commit() + +def get_all_students(conn): + df = pd.read_sql_query("SELECT * from students", conn) + return df + +def create_map(df): + m = folium.Map(location=[41.2706, -97.1749], zoom_start=4) + + # Group by city and count occurrences + city_counts = df.groupby(['city', 'state', 'latitude', 'longitude', 'country']).size().reset_index(name='count') + + # Normalize marker sizes + max_count = city_counts['count'].max() + min_size, max_size = 5, 20 # min and max marker sizes + + for _, row in city_counts.iterrows(): + # Create a custom icon + icon = CustomIcon( + icon_image='./images/BuffaloMarker.png', # Replace with the path to your icon file + icon_size=(30, 30), # Adjust the size as needed + icon_anchor=(15, 30), # Adjust anchor point if needed + popup_anchor=(0, -30) # Adjust popup anchor if needed + ) + + folium.Marker( + location=[row['latitude'], row['longitude']], + popup=f"{row['city']}
{row['count']}", + tooltip=f"{row['city']}, {row['country']}", + icon=icon + ).add_to(m) + + return m diff --git a/webpages/testing.py b/webpages/testing.py index 39f70111c..956ddfac5 100644 --- a/webpages/testing.py +++ b/webpages/testing.py @@ -1,8 +1,14 @@ import streamlit as st +from webpages.SendEmail import send_email - +# # Example usage of the send_email function +# if __name__ == "__main__": +# send_email( +# sender_email="noreply@buffteks.org", # Sender's email address +# password="cidm4360fall2024@*", # Sender's email password +# to_email="REPLACE_WITH_RESIDENT_EMAIL", # Recipient's email address +# subject="Package Pickup Notification", # Subject of the email +# ) def testing(): - st.title("Testing Page") - - + st.write("Testing Page")