import streamlit as st import pandas as pd import numpy as np from cryptography.fernet import Fernet import os import io from dotenv import load_dotenv from google.oauth2.credentials import Credentials from google_auth_oauthlib.flow import Flow from google.auth.transport.requests import Request from googleapiclient.discovery import build import json import logging from oauthlib.oauth2.rfc6749.errors import InvalidGrantError import secrets import time # Load environment variables from .env file load_dotenv() # Set up logging logging.basicConfig(level=logging.DEBUG) # Set page configuration st.set_page_config(page_title="Midterm Grade", page_icon="📚", layout="centered") #ADDD # Custom CSS to improve the app's appearance st.markdown(""" """, unsafe_allow_html=True) # Google OAuth configuration CLIENT_CONFIG = { "web": { "client_id": os.getenv("GOOGLE_CLIENT_ID"), "client_secret": os.getenv("GOOGLE_CLIENT_SECRET"), "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", } } SCOPES = ['https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile', 'openid'] # Function to create OAuth flow def create_flow(): redirect_uri = "https://meng21-gradeview.hf.space/" # Update this with your Streamlit app's URL # redirect_uri = "http://192.168.1.18:8501" # local dev logging.debug(f"Creating flow with redirect URI: {redirect_uri}") flow = Flow.from_client_config( client_config=CLIENT_CONFIG, scopes=SCOPES, redirect_uri=redirect_uri ) return flow # Function to check if the user is logged in def is_logged_in(): return 'credentials' in st.session_state # Function to get user info def get_user_info(credentials): service = build('oauth2', 'v2', credentials=credentials) user_info = service.userinfo().get().execute() return user_info # Load and decrypt the CSV file @st.cache_data def load_data(): encryption_key = os.getenv('ENCRYPTION_KEY') if not encryption_key: st.error("Encryption key not found in environment variables.") return pd.DataFrame() f = Fernet(encryption_key.encode()) try: with open('encrypted_grades.csv', 'rb') as file: encrypted_data = file.read() decrypted_data = f.decrypt(encrypted_data) return pd.read_csv(io.StringIO(decrypted_data.decode())) except Exception as e: st.error(f"Error decrypting file: {str(e)}") return pd.DataFrame() # Main function to run the Streamlit app def main(): st.title('📚 Midterm Grades') st.markdown("-------") if not is_logged_in(): st.write("Please log in with your CSPC Google account to access your grades.") if st.button("Login with Google"): flow = create_flow() authorization_url, _ = flow.authorization_url(prompt="consent") st.markdown(f'', unsafe_allow_html=True) else: credentials = Credentials.from_authorized_user_info(json.loads(st.session_state['credentials'])) user_info = get_user_info(credentials) st.markdown(f"
", unsafe_allow_html=True) # Load the data df = load_data() # Look up the student's information using their email student_records = df[df['EMAIL'].str.lower() == user_info['email'].lower()] if not student_records.empty: st.markdown("---") st.subheader('Your Grade Information:') for index, record in student_records.iterrows(): st.markdown(f"### Course: {record['COURSE']}") col1, col2, col3 = st.columns(3) with col1: st.markdown("ID
", unsafe_allow_html=True) st.write(f"{record['ID']}") with col2: st.markdown("Grade
", unsafe_allow_html=True) grade = record['GRADE'] if pd.isna(grade): grade_display = "N/A" else: grade_display = f"{grade}" st.write(grade_display) with col3: st.markdown("Remarks
", unsafe_allow_html=True) remarks = record['REMARKS'] if remarks == "Passed": st.success(remarks) elif remarks == "Conditional": st.warning(remarks) else: st.write(remarks) # Handle grade messages if not pd.isna(grade): # Check if grade is "-" (conditional) if str(grade).strip() == "-": message = "Your grade is currently marked as Conditional. This means your final standing will depend on your performance in the final term. Work hard to achieve a passing grade!" st.markdown(f"", unsafe_allow_html=True) else: # Try to convert grade to float for numeric comparisons try: grade_numeric = float(grade) if grade_numeric >= 1.9 and grade_numeric <= 3.0: message = f"Keep up the good work! Your grade of {grade_numeric:.2f} shows dedication. Consider seeking additional support to further improve your performance." st.markdown(f"", unsafe_allow_html=True) elif grade_numeric > 3.00: message = f"Your grade of {grade_numeric:.2f} indicates that you did not meet the passing requirements. Please consult with your instructor to discuss options for improvement and potential remediation." st.markdown(f"", unsafe_allow_html=True) else: message = f"Congratulations! Your outstanding grade of {grade_numeric:.2f} demonstrates exceptional performance. Keep up the excellent work!" st.markdown(f"", unsafe_allow_html=True) except (ValueError, TypeError): # If grade cannot be converted to float, skip numeric comparison pass if remarks == "Conditional": warning_message = "Warning: Your current status is Conditional. You need to pass or achieve a higher grade in the final term to improve your standing." st.markdown(f"", unsafe_allow_html=True) st.markdown("---") else: st.error('Your email is not found in our records. Please contact the administrator.') if st.button("Logout"): for key in list(st.session_state.keys()): del st.session_state[key] st.rerun() # Add footer st.markdown(""" """, unsafe_allow_html=True) # Function to handle OAuth callback def handle_callback(): flow = create_flow() try: flow.fetch_token(code=st.query_params["code"]) credentials = flow.credentials st.session_state['credentials'] = credentials.to_json() logging.debug("Token fetch successful") st.success("Authentication successful!") time.sleep(2) # Give user time to see the success message st.rerun() # Rerun the app to clear the URL parameters except Exception as e: pass # logging.error(f"Error during authentication: {str(e)}") # st.error(f"Authentication failed: {str(e)}") if __name__ == '__main__': logging.debug("Starting the application") if 'code' in st.query_params: logging.debug("Authorization code found in query parameters") handle_callback() main()