Spaces:
Sleeping
Sleeping
| 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(""" | |
| <style> | |
| .reportview-container { | |
| background: #f0f2f6 | |
| } | |
| .big-font { | |
| font-size:20px !important; | |
| font-weight: bold; | |
| } | |
| .welcome-message { | |
| font-size:24px !important; | |
| font-weight: bold; | |
| margin-bottom: 20px; | |
| } | |
| .stAlert > div { | |
| padding-top: 15px; | |
| padding-bottom: 15px; | |
| } | |
| .message { | |
| font-size: 18px; | |
| font-style: italic; | |
| margin-top: 20px; | |
| } | |
| .warning-message { | |
| font-size: 18px; | |
| font-weight: bold; | |
| color: #ff9800; | |
| margin-top: 20px; | |
| } | |
| .footer { | |
| position: fixed; | |
| left: 0; | |
| bottom: 0; | |
| width: 100%; | |
| background-color: #f0f2f6; | |
| color: #666; | |
| text-align: center; | |
| padding: 10px; | |
| font-size: 14px; | |
| border-top: 1px solid #e0e0e0; | |
| } | |
| </style> | |
| """, 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 | |
| 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'<meta http-equiv="refresh" content="0;url={authorization_url}">', 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"<p class='welcome-message'>Welcome, {user_info['name']}!</p>", 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("<p class='big-font'>ID</p>", unsafe_allow_html=True) | |
| st.write(f"{record['ID']}") | |
| with col2: | |
| st.markdown("<p class='big-font'>Grade</p>", 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("<p class='big-font'>Remarks</p>", 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"<p class='message'>{message}</p>", 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"<p class='message'>{message}</p>", 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"<p class='message'>{message}</p>", 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"<p class='message'>{message}</p>", 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"<p class='warning-message'>{warning_message}</p>", 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(""" | |
| <div class="footer"> | |
| Made by A.IBO.JR | |
| </div> | |
| """, 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() | |