MENG21 commited on
Commit
ebafd8b
Β·
1 Parent(s): 98b48fa
Files changed (2) hide show
  1. app.py +133 -68
  2. encrypted_grades.csv +1 -1
app.py CHANGED
@@ -5,7 +5,10 @@ from cryptography.fernet import Fernet
5
  import os
6
  import io
7
  from dotenv import load_dotenv
8
-
 
 
 
9
 
10
  # Load environment variables from .env file
11
  load_dotenv()
@@ -27,9 +30,45 @@ st.markdown("""
27
  padding-top: 15px;
28
  padding-bottom: 15px;
29
  }
 
 
 
 
 
 
 
 
 
 
 
30
  </style>
31
  """, unsafe_allow_html=True)
32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  # Load and decrypt the CSV file
34
  @st.cache_data
35
  def load_data():
@@ -50,75 +89,101 @@ def load_data():
50
 
51
  # Main function to run the Streamlit app
52
  def main():
53
- st.title('πŸ“š Student Grade Lookup')
54
- st.markdown("---")
55
-
56
- # Load the data
57
- df = load_data()
58
-
59
- # Create two columns
60
- col1, col2 = st.columns([2, 1])
61
-
62
- with col1:
63
- # Create an input field for the student ID
64
- student_id = st.text_input('Enter Student ID:', placeholder="e.g., C21101100")
65
-
66
- with col2:
67
- st.write("")
68
- st.write("")
69
- search_button = st.button('Search', use_container_width=True)
70
-
71
- if student_id and search_button:
72
- # Search for the student in the dataframe
73
- student = df[df['ID'].astype(str) == student_id]
74
-
75
- if not student.empty:
76
- st.markdown("---")
77
- st.subheader('Student Information:')
78
-
79
- # Display student information in a more structured way
80
- col1, col2 = st.columns(2)
81
-
82
- with col1:
83
- st.markdown("<p class='big-font'>Name</p>", unsafe_allow_html=True)
84
- st.write(f"{student['NAME'].values[0]}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
86
- st.markdown("<p class='big-font'>ID</p>", unsafe_allow_html=True)
87
- st.write(f"{student_id}")
88
-
89
- with col2:
90
- st.markdown("<p class='big-font'>Grade</p>", unsafe_allow_html=True)
91
- # Check if grade is NA
92
- grade = student['GRADE'].values[0]
93
- if pd.isna(grade):
94
- grade_display = "N/A"
95
- else:
96
- grade_display = f"{grade:.2f}"
97
- st.write(grade_display)
98
 
99
- st.markdown("<p class='big-font'>Remarks</p>", unsafe_allow_html=True)
100
- remarks = student['REMARKS'].values[0]
101
- if remarks == "Passed":
102
- st.success(remarks)
103
- elif remarks == "Conditional":
104
- st.warning(remarks)
105
- else:
106
- st.write(remarks)
107
- else:
108
- st.error('Student ID not found. Please try again.')
109
-
110
- # Display some statistics about the class
111
- st.markdown("---")
112
- st.subheader("Class Statistics")
113
- col1, col2, col3 = st.columns(3)
114
- with col1:
115
- st.metric("Total Students", len(df))
116
- with col2:
117
- avg_grade = df['GRADE'].mean()
118
- st.metric("Average Grade", f"{avg_grade:.2f}")
119
- with col3:
120
- passing_rate = (df['REMARKS'] == 'Passed').mean() * 100
121
- st.metric("Passing Rate", f"{passing_rate:.1f}%")
122
 
123
  if __name__ == '__main__':
 
 
 
124
  main()
 
5
  import os
6
  import io
7
  from dotenv import load_dotenv
8
+ from google.oauth2.credentials import Credentials
9
+ from google_auth_oauthlib.flow import Flow
10
+ from google.auth.transport.requests import Request
11
+ import json
12
 
13
  # Load environment variables from .env file
14
  load_dotenv()
 
30
  padding-top: 15px;
31
  padding-bottom: 15px;
32
  }
33
+ .message {
34
+ font-size: 18px;
35
+ font-style: italic;
36
+ margin-top: 20px;
37
+ }
38
+ .warning-message {
39
+ font-size: 18px;
40
+ font-weight: bold;
41
+ color: #ff9800;
42
+ margin-top: 20px;
43
+ }
44
  </style>
45
  """, unsafe_allow_html=True)
46
 
47
+ # Google OAuth configuration
48
+ CLIENT_CONFIG = {
49
+ "web": {
50
+ "client_id": os.getenv("GOOGLE_CLIENT_ID"),
51
+ "client_secret": os.getenv("GOOGLE_CLIENT_SECRET"),
52
+ "auth_uri": "https://accounts.google.com/o/oauth2/auth",
53
+ "token_uri": "https://oauth2.googleapis.com/token",
54
+ }
55
+ }
56
+
57
+ SCOPES = ['https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile', 'openid']
58
+
59
+ # Function to create OAuth flow
60
+ def create_flow():
61
+ flow = Flow.from_client_config(
62
+ client_config=CLIENT_CONFIG,
63
+ scopes=SCOPES,
64
+ redirect_uri="http://localhost:8501" # Update this with your Streamlit app's URL
65
+ )
66
+ return flow
67
+
68
+ # Function to check if the user is logged in
69
+ def is_logged_in():
70
+ return 'credentials' in st.session_state
71
+
72
  # Load and decrypt the CSV file
73
  @st.cache_data
74
  def load_data():
 
89
 
90
  # Main function to run the Streamlit app
91
  def main():
92
+ if not is_logged_in():
93
+ st.title('πŸ“š Student Grade Lookup')
94
+ st.markdown("---")
95
+ st.write("Please log in with your CSPC Google account to access the grade lookup.")
96
+
97
+ if st.button("Login with Google"):
98
+ flow = create_flow()
99
+ authorization_url, _ = flow.authorization_url(prompt="consent")
100
+ st.markdown(f"[Login with Google]({authorization_url})")
101
+ else:
102
+ st.title('πŸ“š Student Grade Lookup')
103
+ st.markdown("---")
104
+
105
+ # Load the data
106
+ df = load_data()
107
+
108
+ # Create two columns
109
+ col1, col2 = st.columns([2, 1])
110
+
111
+ with col1:
112
+ # Create an input field for the student ID
113
+ student_id = st.text_input('Enter Student ID:', placeholder="e.g., C21101100")
114
+
115
+ with col2:
116
+ st.write("")
117
+ st.write("")
118
+ search_button = st.button('Search', use_container_width=True)
119
+
120
+ if student_id and search_button:
121
+ # Search for the student in the dataframe
122
+ student = df[df['ID'].astype(str) == student_id]
123
+
124
+ if not student.empty:
125
+ st.markdown("---")
126
+ st.subheader('Student Information:')
127
+
128
+ # Display student information in a more structured way
129
+ col1, col2 = st.columns(2)
130
+
131
+ with col1:
132
+ st.markdown("<p class='big-font'>Name</p>", unsafe_allow_html=True)
133
+ st.write(f"{student['NAME'].values[0]}")
134
+
135
+ st.markdown("<p class='big-font'>ID</p>", unsafe_allow_html=True)
136
+ st.write(f"{student_id}")
137
+
138
+ with col2:
139
+ st.markdown("<p class='big-font'>Grade</p>", unsafe_allow_html=True)
140
+ # Check if grade is NA
141
+ grade = student['GRADE'].values[0]
142
+ if pd.isna(grade):
143
+ grade_display = "N/A"
144
+ else:
145
+ grade_display = f"{grade:.2f}"
146
+ st.write(grade_display)
147
+
148
+ st.markdown("<p class='big-font'>Remarks</p>", unsafe_allow_html=True)
149
+ remarks = student['REMARKS'].values[0]
150
+ if remarks == "Passed":
151
+ st.success(remarks)
152
+ elif remarks == "Conditional":
153
+ st.warning(remarks)
154
+ else:
155
+ st.write(remarks)
156
 
157
+ # Add personalized message based on grade
158
+ if not pd.isna(grade):
159
+ if grade > 1.9:
160
+ message = f"Keep up the good work! Your grade of {grade:.2f} shows dedication. Consider seeking additional support to further improve your performance."
161
+ st.markdown(f"<p class='message'>{message}</p>", unsafe_allow_html=True)
162
+ else:
163
+ message = f"Congratulations! Your outstanding grade of {grade:.2f} demonstrates exceptional performance. Keep up the excellent work!"
164
+ st.markdown(f"<p class='message'>{message}</p>", unsafe_allow_html=True)
 
 
 
 
165
 
166
+ # Add warning message for Conditional remarks
167
+ if remarks == "Conditional":
168
+ 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."
169
+ st.markdown(f"<p class='warning-message'>{warning_message}</p>", unsafe_allow_html=True)
170
+ else:
171
+ st.error('Student ID not found. Please try again.')
172
+
173
+ if st.button("Logout"):
174
+ del st.session_state['credentials']
175
+ st.experimental_rerun()
176
+
177
+ # Function to handle OAuth callback
178
+ def handle_callback():
179
+ flow = create_flow()
180
+ flow.fetch_token(code=st.experimental_get_query_params()["code"][0])
181
+ credentials = flow.credentials
182
+ st.session_state['credentials'] = credentials.to_json()
183
+ return credentials
 
 
 
 
 
184
 
185
  if __name__ == '__main__':
186
+ if 'code' in st.experimental_get_query_params():
187
+ handle_callback()
188
+ st.experimental_rerun()
189
  main()
encrypted_grades.csv CHANGED
@@ -1 +1 @@
1
- gAAAAABnCzrPXYAp2HAr-Wi-1W690mrpiRBQZLDySaLU3sHJkCVVbqnB7kZS7suaoOZ4s896SfoOcaCfd727HwLX6nPcrvYJvTEJIlEV2-rsE3og2FY3S3tG73OxXSG9JDxAHTZHNxuaMHFJQhJD3ChD8wwA7ABA3drFPXy3LMWxDzLRm2jtH9f5A8ovudBZhPmQIEV-rSfWwd8OTD71BsGDvJz5L3Ov3Gv1BWVZ1-K-4DvwU_pEwAG9_20bfjL_qFsGKPA2dve6aY-Ero6appg2w0GagMWaa3Yk-KERmVKBkLz5gBfkB_CFW1iLU--jCdCGao47TYMNF7YG-OTe5b-9lhhp0Jkn60jPmsVcDeDQ89jvHu5z1BgsAm96TsWx3sClQ4EcVTkXxEmp576H7bHm9vsYKsX47mJ5WH5IIqsFEhxj6DZ4x6koGUBLho0ns_qS9CsWXxBcuOT6pF6DmsY1dlYLeakezOotBjZtk2mjCOGJkWT-k5KtcOcPkXXrIh9X4PUwp8Y9XOoEYdgkJ4kCo4MTyi0nydY0V2QaWpJpKGxuq20NDlHKCm9OVx5bA6oH6td2efYY1PAAxzwLTwYAoPD_yiOypC4qAYujcCMPmE4xe5BYqb8jJH-sdUbg9DH4ichQ2ce6Fz4Wrvw2g6UHJ2wUmNIGtLh_fdnCSWrVlegsZr2gIXgbVSDNi-VTXSkzsNUoBHxqddD5axIzw2XsfkeGBDkbuIw4zjkyXJhfnxOb6SRKfgqFQogzxPCX-ZsLuzlcLINLKZb3ys7HUwySQ3uNGem8_whqFrt7JmPFRfjokYe9Epr1O-0xsrrkFpfVHOhyJOIq1LVOzz2dDZ3nVCmHGeXHTqbXWlb7aWJUmGy-fWHcEzKErzPGsbbENdQ0hkHnMAgxuZS4c1hA6Y6QR-4eD9_EyYQmrfY2XymzgjpqUwGjFPT7kxfNhZEIjPn0nyCwyTXCa04b3vC7N0nRqWiQwHaBIohUrgYZRtHzmea6L8BbvU3S7xIe_XLubedZVHTr-IwQ6QBF_jgBbhnPKKzvzVXesD_OD4T_Uuv03OBHemZRqwNtlcrd8YjR2mMSCUnWSPlloDRylurjLudzjFFLCwlUXfhyED6Cfc31fYIPAP-yuRtxdkXLy8Kd3LpBFBP9WWsGhxvQ0U9vew0Tg0JnriwnvXSkv7aAr4Bq3iFwadl_krdhPtZ0iRgKY8TxE-TtjGqMqjw9qJ9YmVjHAvspS30LAb95tavfHIw8Iu95SQP7Ut2ckxR-iFZJfmehb5pP4dDwST6j3FOPVTz-q0WvtFJN5hLpVGtHgyJpt76-WTYR4KOJM3OmLnfp61EDk4egL-oMCivXOJtJU5Xk7nDGGEYigHpVpr0dXnwYkuPl24wa9hM5Lqee_j1A5vkgv13x_8e3MptgL1ebwbAQmhayTVeQP05llM_wQlz41sSocseY693v-JnpPl1Az-ja-1CP1qDNnoO_kApGo1nEWSUC20pXi05p5W_CMhU8Z2eDAZHjnhLMxHftMDLlM8u3hX4XukxHxpKU4WKHLSibnPGzhRL2s-Oat8g5VTHqnzUaT43AEl97800NOG43uJ05pjW1zaH2XwrmvvhFiurz0LmkE2nBV2Hd6zI2fSXjYcDzQRWNbbbHimoLQwFwLqntkjb15XhM6NlsS8GswidGXO981WH-3qQS_1fVEOtoWDUbRLR3AJZAozl69ga5J5JljHO2uu3jG2zOhR2F9H1dEaujATc5wvrdCPjtRiXsjnyB9SVoK_YcBJJGp7L4ct_sWUsr4f8UT5SeklkLpCcgkPdlX5fEjpkgBUga_yn5KRp8xwRd9vzMlYl27NIftbPgZzeB1MMbMer_Ownx9LYjY_sRuTSrJYw_aldPQGCsEUi1nJBFJHb3irhuPWEWu8EmmupSTXW1cXYkyb7UDDuA6X4o9mCijwWFy8FTl2O_EC5uxWwTL2DLZJCjY0v_vK8htwaqTemuRFS1V5x7zNYiy4DPvuL6vKjUno5qLzIHGOcfHtZ8cGl-UdvdjpKCocVIX8BmO8AcUtVSbarGotunYTVYQZGmcT-qbEJ4GNuxnwJ1ytG9JpJH90NVUq9TnAJmoleqZBOxGrg_SLR33UTeYym0-StLQ_z7MXLJ8a7ITVn82SjhxcJZ0q4OzxH-0OtyRQjezXDEDScPq3_o9wlAa93IjzfmRYh5mECeejnRa2JR0KLdGfcloL75pRUyNeQAAN74Smqctxe6a6ppNljH9Hkol8NB_ibqmo2s_vS-Kwd93Y5m2WO_yq-ZZjiE6ymhrAP-raQtB2r5rYLFXS7paQJKu5UOpFmVzDTolpe0y0nDa0_PPvAUDKQIJ6tFql6Yt-ZwSbjXDl6iJHaxmBIFVVSBvgMlf3cFC4W1mo8w00T1HPclxWq15hHekywz08FFQ1D2X7F3FrcPxo-vuwfNTG_0rk1B5hHJNRvMi_fOu7KuIHRaV5NWuuV5baakbKnPpaoLwKQFrA4TJo84_xU3nkrvWvxY1r4sDeYxSizMBnvrdv8vVy-YsVcBcoES0f0Re9harps9r4M1K78O9fS_GU9xMC9rFlqFEfB90R1Urslm8-0niQWOyUp5a_yQvFCrFn4SOFV9ka_-unYlZ0lRVo9rUM_3LbEhBp7ov1qJSUoOpnjuIxFdlgiYI2WOWQUp5WaFoqTeZYgW89qhe2vawT5oqTpbYCPtQdWUqX5Eyhc5bskoDucW9g8Udv-TFncC4bE3vELdvySbvGVfAWSkTAgPHhGzT2LPU2KKf_maVjUi2DPhRwRGqlelFq81XCEizWuXFDpKApMJjRmPTR1l4ROJMF9EJdEFAevR14_ESMUDiGk6sQxcW6p-WL2SMIBNoIT0lgOrjHC5Z8wpmQFH3aBvkKsT9oUvAeAWjsthn0a3j5VPTIQOUcjcOG8L25gDJ2p58wOEJpOKtSGwZWAEJ169Xh5EVzr-6iLutQUWQrS-dLMXsQHYDcU08OHC6WmJJRa3iSKdWpHkwAECxdNDCMgnYkB4JMlqaye3pLr-Kzf6IrXVVebPPRwG8mMQGaPe1bBRvMG4R6VpDlXU5n0Sc-mHwuXvgw5IoocXmsbguheBELM2ZhcC9v0YpV_yPTObtEbnm8bbcLM62kiycTbueT6gW3viSyOfqEyT3buFIqMnYobjjRlg41H_yTUOdsrHO5G7criQ0sASodOP1VvpHhwkneWKMJbxnQrRnUCYtAsrCnDtnD1ntzABYt5xl5Kz171mosqIWZdf9vCWR9saZSLAZnPDz_f37iuzPtm8dGM3D1B2XaTRKo_YeHBGRe7HAiADdeihVaT_C2tQG4dADlksNX0e6j-irzTxIwt5GD6uqYwX71fHi_YalVLOXVvI_1HmcP_pRXW5pdDXVI-aGI6JcrmwM_8KP9rodxQKKtC6UM6yeid6M9d6Wyvwo9LV8zAjWHwGLU0xvy-6ku6zxUUzJ64UKOokN2T0Kf6Zk3hkLBv0nr6q0oXN8tgkewCYEZTwZx4PAVMK1dHm1BB6WmYojxYDDiyoMVBM0ANDRwsUTaajQmdyR0dMjAwk4FIrQtxtutZx3_28RJbdE_acSI-yVS-dn3y6s1ekdusIpqxoxRc-zCC8DOSs-rhoLmmM5JPUlD78FEy9EwGHZxtQFp7baB-bdWHW-qYkBZR-ROTiQAriAiofHlS1mAp1ONch07_xv7mIbcK1hV9zI08KO2yitsFVrR_VBx83OCtraWEsQ_8V-0pxgolFBQkFeVrSVGFIBiLpZc5heF76tTwk91NpiVaDgROReR_i-2NIj9xyO7fN1qhyEJr4DUU9v2-qjCeKbLy92Bv4BDEkjtAxhfc-gAAUON5yfhhyAIWBjIKk9IN0isjm532IlG6mkM0NZqSmdVsKTix72UPOZrsHob6h7ti7zxh1UA5PyyfEQOSbI8bwjvmXaUtN7f4JwN-MsvNTkM4JHK7PLG_QFSv859U1HBWhtWWHzwF4ARerOlbcttniXa23Rv1YFYfseOH35jrOM3_BzitbaeqNCVt34QL96WM_RoZcHoi2UQvWPxQRhIwwDaFQRlPN4dIHVsxEWrw_Sui2-lv6Cune3OLcls55iPZszHNyKFgcpk9pa6LGoQXQGC0a2C4t8UCmruId6Pf5IxOZhwr8k3bzhcZx-VS6oTV53Qn8LJY1XR1hJH5us9uE459Si6EAe9PPepihRsi8nbw8sZi1CCPCUNd-Yt0BY8qXMjKOZjYHwxnTwS9RA47evksL1velm63orM_a1pA16oNSv56vRpdnEOeiuRvVPfaNZiapqMcUi0zw-7HsTlZF9gZbahFd64aWAFLya8CjrqJggzs-HzrIM1DQ2bqx20z2N2P_JTI7MtmmcRE8SZAUKxUlxdU_jUj_mdVo4xPhLKqoPnbJ88m4T1dcbLjY6pinNsEn2Hom7FKgb-8rl4AgBx-ELZET15aw8F2JLxTbgT_TmP8dnwp-dMhYxXQUHbUshTrDJCrivDuuDNMkYYCfYztWumzSleawasktqxH6pMAYyf9Y0KCtAMhYYveRRted8-afgNhio9hV1ThHYAbVmbW9p7mFLAyVisBKrFv32EHYM2sIChH6HlaVERk7XITD3ZI5G5lglkt0z3beG2PP_eGNaDXQwIbE1gqBwyj0NjriXHqpOLExQ3DNKHZc23J128moSybw91IAc5n4r59Z-Ar67J5xEXjTp8fCsiH6vTj030KP_f5PYuh3JVi_YmgBhsZY0Rt0mGqCb9K6d5VCITPFPeiWr6WzkG9MZWK0VhjK2lol4PF4rHAwTH0gOo9lICTLlwyDs6UTX6_TmjcNEDinCGCEPYImxQT2I-zVUOgvTgOFCfbKN8JnI6irJ5yxFxCMTBteNGvzwpi58GZKzyr4wOjAznHOGj7bNw4QOmJgN3GGQm0sqypZ4cGsW4KSJQNJ-mIJFUehqlNRuCbPhBzYLi9BjyGBuwD9_sH8r80s
 
1
+ gAAAAABnCzvQkdijpH0UWU-RFltkNepJxjW0BPNe4oq_z9UAdEthgq-Mo8uV18Akp3Utsif5XaULqMqRWOHMn6fuh3nzdorLwkx2NTTTAJ9lFcY_E4eqpWb5npvMkIstJPKZfuOk98u0hs4BDdL0s2ZoR7RWDGM20SSINqoypdL4rUEb3I4xhKLOC1qTcDVvYCDzPLhEr4Mp_bgEH241XfflcVdr2Ak940nJk8gPTqW-TYHG_m_VsmEq5Fu2tpv-UKcthJ6jsH4HjPbp_Z25tZJUJWhHGlTN5V_kmCpPbDQKGSOm9xjoelzLQcfxPSvqJAFfn5ky0XhdZ69gGTXDePZ6ghCjuORc68VsLUuBjvoiBhXZ0RRcka0QQqw5PQ2OLZZjnHjc-8YN2QKMqAr9Fqr5iHPL46ISmdhQhiE4X49T8n-FHTVhViOhl4Z22CMk9zSdJwPV5qw-3S0gAfEDZEnFlzr84AqUiHbVIG08QiQqvZUrh73HCytrBuh_cvGBxf-VlMwETfLuG5MdWYRyKWOwyKz1GAThURgSrAI8AxJa8lnbt6955R82q6yaj4MiqQaW09xNKkRs0V8exntBOGixR5bR5dxY9s6rjk9p94nXuu1ua-FV4e6AG15Dvk3xib68trXkwfJIONY5QiM7-yD5vb7XbFO4T5V3MQAqUvhseGTIdnkyNdd5vz7SHVyWW7hWqjLcuM0A2RuO7P0WiRsJd7UgjvbCns1rmdkc7c2INVNp8BC9UUk7F5vCj4Ekqpxo8i8Z7EbL-81sBLV4aGIcbEhIJPoKMYuJTzl4_lJkg6CQ0hy3NuDBrSaDptngG1J7J2CfOC7GCA8HEiDpmMg_K_FdiK2qLKXvBryFmwfgiuOPFzXYxsgrB8HpTJe83VikG3sqnLGDBOOo90UFW6eZQrRNWsotJXgz7PaVGIGmmPRzX0Q65YOVSwfM4pZwaJxTFVQV5PKexg4O2hRWH8NEgssKQck5OqnA-iBmbhP_MsF3ZhPTsz1ocQTiwYc1msVNUTjauRNf9Xtf-NvakbiXHpPOSgO4oWWJnZxitUS5YvH1WD8rom4P3IapviF5tIJ8TjJPUE3JxyksU7M4egvTOHHzKbiprQ1VBgJY1CFfjGr11l_BSLQfOyIXlkmTU6dZIlQj23jRDiAvP2aSVvGwzZwDhWOJLxSIopeBKaEKSXbrU-4HNpYU2ff2b5Z-S21g0Uq83Of_k0t2v3XloUWRuL1AEcsAPVkF_v_xKwTP6obeYtAIEKTqMWGluUOvDczIrP1HX2O2_NC9QpzG2PYJKbu1qSRiPccXi3ABfHieOsIweGb7tIxniDiyQrelvUb9kTQYqx9e7xMS-L1o979ZsahjMXTT1H2OEIex-qiuso5ya6KBioFqs2nW_29WShXyeScsJo1rqR78QSoCcNRiAVfiqclspeDThA6SJaV0Z0EUKZGPiOoqPl8spbPXldapaNCBwy700xCRCvECKlp7a-pH048n4QhoamdB4Xp9rxwkD2CBpLT5GxRD_lSJpQpbLo4hxWs3maOTkAQkLC32-l_BmkF5WtoBr4NyP-boQ6AufhAWiYDEKca7RTqCjv7J0JXLGwzgCpbHCNqvgD4P9pML2IQGZma12D070hQeqhJXz_dagOt-dcL4EYENENvfXFLMv31sxOWLFX_YJYk7M3493hx-vAcO4RuHZUoZXRqSJs0AHqmnVABAE5qY4bOCKy3zqFV7iEIXTzbLrGDlz0lK0gF0H28eTvD8n-BlM-qumDOA9iX5Te7MlNIjjQUk17XN6jRcdVKrcx4nVj68zKYywucbv4UnGKrIl26RYYVpduVvQIdTg-AZ1brD6-unM_04RRwSbJdfUhxsPik_UBCSeuWokmHFzQSDxF5wnpJLync6_pGeXh9y8sJxk7rt71lzh_kh1oIBdg7SSU5esuTbTY3jL1AdNgA8wraxFII-uygP7Ljm49bUlvUl0zN3pwM7qIgDc00RHYwzoa4VslaS1g09Pqc3VX-oRukQzLAMczpq17ovFQvipHWYinSjly486HWGuktK-b1bMQ2fgD5BYqJYlzI1tpZfK9enozUr1EEbFg7fJwoRCBrt8Rt-HfCqvoctwZXS3JvW-v6uf81y5RIHdCsiBVlCsJ88PFT_F84yGw7Ka5fedd5q3FBHyCxjBB_k_2FAd95phRbGrvOcvGI4Nei1X7SZOwJSR50Hm-bMtb9jbTL-zNMF1ub8P7gPeF2LIBVWnYIoj_Ncj-My9Ett75J03bc6x_CLW5VDqLJHx63PJ1Vi1wLkkkk7EIbJCXAzw3bmf4o2iqQg2EqCGNJQqlryMGFtKlloO8bjmKep9ln9fdMg9nj1Je-W5jiVA5M4ks5RV_FnasYJj7zZi4eaAPVMsMSFIf6U1rUMlfOZkLJM_lpOfavmZCykcFq9p0LlfnM8PzNHvEmlcFXC8tiwtxBgpxZk38S7XK-8uJQq4440aAMwNOKz7Qm24WxIKf1KuNBCPmRpGTGaSTJLEWnGRWo1Dxv7fXQt4llAm-EbNOUB5LpSclxcosZeO_w20-D5BzjsVlHq1DT69OZ16MnBrVcnOGOfKNAGWGD2ctDvsSzjF9f7j1NaSlZQs2RhuHqOEpe_bYoC-w-Wn3GoAWVhfhq0WGSI-lhzD4kgv2FLUgAG-7b0MGXbD57gKSmz6TKcKq82k3JmUcozx2g13dy9uJW7tDrFN2QkLnfTsI6JSJwf81M8EnLAv7l7p6C8lp6k2FteNX3b6hYTe0UUzJy8JVMgiWpdpt_kn0X1F59R6au3Iq30fUU7d7yieSwdgbRbb4KZVUQBaxK7bclIm6S8AturXJTyYsZxCbd2Doz-RS6AcDT6_cWMmqITwYkGgOLLzIfTpvLhtyDJcmGH7aRJjQuVZs_De3h4kLn3VwRrf2YD3L57tZa6RyStlYsp87ZksQCtC8fgzsZr95INq8nQmRnMT9hGIAACYBspTPgMdoUPh2t37MACVSUT_Sc4XFxERL4mIzmy-PuKi6OinXQ4TLu9dr-7bU0QS-R79YWvinKE1ZWaDp3fw6bOMeOrNj39YVGTrUYBlWXf0-dnqafduSIiIY7z8K6y7TqM-Jp4su8ePjr1a5xaVRQ8D5tik4m1IztgT8M5Cq9wq2nklW_u539SwJ6HcVs8CVYPF25TqtfxKn4huLOviAEaHQ5VwLDYdt0jQ94BShdk94H_vG5dnMri_Z22GdjnZHJHT5S4nJp2YCVME7ihO6YmpNUwExOWOlNROiYxTu-4faf1AN-IsCB1DLicEoyLgF8xTL5pB9fhdxJwqduqmDMGR5zipcl0GbMJzAx7PHc0kSS5sfPiNQc9WuUcI4tZT-FXy0WwqtHq018ounp_GPfqJ467ET2iSijh1SjET7I8uUIw7xQFKxbYINYDs1wnWh4PY7ym-LQvOEZRbUG_j9FRMy2rBoz-C3kK8E7Rl6dAv7QmyYKaLnMY4Q8o4i65lhRUpOzhCjiqkWJvUF1c-WVQmUdjumCviyo4yjn_6K5vSP344FKlGgw8CxZGlh4r45j-7yRncZ2NSlg4DFKFZaPMIf4-_5-_LflVLGP1NSElRWbqTSIVB0ZqjAeCXasxqFSWceoE52IHiOiHBAgM_C6MWkB6_3-2NhZjAV8GecOl6Wutgcwgm9jtp6r4MD3JkQeogyUrMT5KQl8hh9Jd58UDMGQYmgQqrST7JnSoz8rBa3n9H3YkZfgM31jBbBpiihmcS80NjIJCJjFpEqTnZ1ILUbfBBxDgJGlrk4NTuXtz4LRAB1oz2kXgwDaMx8uJjahS3az_1pBcq6amEX7FK0bnItBu1S-CMw14n-PW4wFR_P1HXKfvRyNifupQfqwyQ3gn-pcm22vUeIH171wNWe0odMrUgJ_NS1V2X4aC8sb9v6ZYfMVzPzKX_RUkM4gv0AMfC8wcECji9_5xViZgF4N2XR4TqX3KlD-WqrPUJZl1shQfrbal_ylhhBJFsM4OtH9UFjCqmoIZehHyTp07iZRPbqSPR0Z-LbubBs--L0PaR2YnQSr89XVC4JHMQZNzZ4LrlZA0LHI8EMs9GG4Ic9kGKVg03LiBDb1vyb42LIeizojwBhHFwVtMOgPLSNW3DU9Jk0NFM7ymC7bDpQ0uwLtFsImAGTivhs9JqMh4QjGHwpUWzgIPpxGLOuyFNs0Id25L5rdh06X4h7MvEkRrwXXNhUZz8eMWXbI9VX_SUVpYF8JQwWDmOK7aV9lCrTQIQ7Nt0if5GMark-UKJygb29ZOrt5NyGUDJ8wth7VQNLFirsKA_o653K_RWDu85vkH6RpfgjvFYOFY5fsqVYso0bT0_ieYIk-PN0w1gEEHiCqTzLymci3eB4YTGv907WyHJteMjL2sgWy-GqQJlh0PXUx-xKmSLaZP46-ylm3956xTnnsoHmOKAOpe9BGTL17D2hciWfyXPv2qwam7Dy6tytG6DB2g-efKM0kYwqohKCtrCmZDqnYuZu1y4nyG-KRWRprAwA_CmuOU_DLDHvYdMIOyCGwW1bew9zqfHbqjQMVVYhTNZ2-hxPwzj9yOUxP9AICHFldHmuqHrow2JPMPJ4c5Yc98OJDOTSMlV7A5UROH9xNnnjE6bDvYBkKcqY45HVbhsZYalCqfVrrCvIemYh3VdSW8qaeVRCSzTJvY9r8JcKGn9SqLKR82mO_eksrDcoxcVtPT1eb3Ocmkc7MTvehGvHFJ8L76rv5qdQ4Pd5qoiJe9aL-k5E5-TdFYHJzNU17NbolUQJDG4684O8jMHTd8Q4ZOT6FN8L0snahEgm3AbvSdawzDDz2BnMVWVW41CVKhg85oCgTdMz8L2KA4HxUortPdkdGUhjte9IPRPFodACJTlrEbFJRrg6eyUWzxWEQv0myC1ay3CQR3FpRlonBtImPTXkvqVS4XuGID3pbF9rxxJKbHkxgvtzJom8I2iCp1h7iO_mMo1_XUGvevrDqHboX0yeFsz2Y-QcMTeNoZ4UaMMKkWXqMD-9bb867TNB4hDpt2yLK1xZv0DSKTz1iJ4b_DAmosp6Unnn18ncd8AzQM5hSn5Q==