Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -15,23 +15,23 @@ st.set_page_config(page_title="Dự đoán tốt nghiệp đúng hạn", page_ic
|
|
| 15 |
|
| 16 |
# ===== Custom CSS =====
|
| 17 |
st.markdown("""
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
""", unsafe_allow_html=True)
|
| 36 |
|
| 37 |
# ===== GSheet integration =====
|
|
@@ -44,35 +44,35 @@ def get_gsheet_client():
|
|
| 44 |
|
| 45 |
# ===== Save to Google Sheets =====
|
| 46 |
def save_to_gsheet(name, student_id, major, prediction, semester_data, sheet_name="Trang tính1"):
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
|
| 74 |
# Define headers based on the model type for accurate sheet saving
|
| 75 |
-
if sheet_name == "Trang tính1":
|
| 76 |
headers = ["Họ và tên", "MSV", "Khoa", "Dự báo", "Thời gian"] + [
|
| 77 |
f"Số môn không thi - HK{i+1}" for i in range(6)] + [
|
| 78 |
f"Số tín chỉ không thi - HK{i+1}" for i in range(6)] + [
|
|
@@ -85,30 +85,18 @@ def save_to_gsheet(name, student_id, major, prediction, semester_data, sheet_nam
|
|
| 85 |
f"GPA - HK{i+1}" for i in range(6)] + [
|
| 86 |
f"Xếp loại - HK{i+1}" for i in range(6)]
|
| 87 |
|
| 88 |
-
elif sheet_name == "Trang tính2":
|
| 89 |
-
|
| 90 |
f"Số tín chỉ đạt - HK{i+1}" for i in range(6)] + [
|
| 91 |
f"Số tín chỉ nợ - HK{i+1}" for i in range(6)] + [
|
| 92 |
f"Điểm trung bình - HK{i+1}" for i in range(6)]
|
| 93 |
|
| 94 |
# If the sheet is empty or if headers are missing, insert headers
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
# ===== List all sheet names =====
|
| 103 |
-
# def list_sheet_names(sheet_id):
|
| 104 |
-
# client = get_gsheet_client()
|
| 105 |
-
# sheet = client.open_by_key(sheet_id)
|
| 106 |
-
|
| 107 |
-
# # List all sheet names
|
| 108 |
-
# sheet_names = [worksheet.title for worksheet in sheet.worksheets()]
|
| 109 |
-
# return sheet_names
|
| 110 |
-
|
| 111 |
-
|
| 112 |
|
| 113 |
# ===== HEADER =====
|
| 114 |
st.markdown("<h1 style='text-align: center; color: #003366;'>🎓 DỰ ĐOÁN KHẢ NĂNG TỐT NGHIỆP ĐÚNG HẠN</h1>", unsafe_allow_html=True)
|
|
@@ -123,135 +111,110 @@ st.subheader("🔢 Chọn ví dụ mẫu hoặc nhập thông tin cá nhân")
|
|
| 123 |
|
| 124 |
sample_option = st.selectbox("📝 Chọn ví dụ:", ["Không ví dụ", "Dùng ví dụ mẫu ngành Công nghệ thông tin", "Dùng ví dụ mẫu ngành Kinh tế"])
|
| 125 |
|
| 126 |
-
#
|
| 127 |
sample_cntt_example_full = {
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
}
|
| 140 |
|
| 141 |
-
#
|
| 142 |
-
# ===== Ví dụ cho mô hình "Dùng mô hình đơn giản" cho Kinh tế =====
|
| 143 |
sample_kinhte_example_simple = {
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
}
|
| 156 |
|
| 157 |
|
| 158 |
-
# ===== Ví dụ cho mô hình "Dùng mô hình đơn giản" cho CNTT =====
|
| 159 |
# ===== Ví dụ cho mô hình "Dùng mô hình đơn giản" cho CNTT =====
|
| 160 |
sample_cntt_example_simple = {
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
}
|
| 173 |
|
| 174 |
# ===== Ví dụ cho mô hình "Dùng mô hình đơn giản" cho Kinh tế =====
|
| 175 |
sample_kinhte_example_full = {
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
}
|
| 188 |
|
| 189 |
# ===== Thông tin cá nhân =====
|
| 190 |
if sample_option == "Dùng ví dụ mẫu ngành Công nghệ thông tin":
|
| 191 |
-
|
| 192 |
if model_type == "Dùng toàn bộ đặc trưng":
|
| 193 |
sample_data_personal = sample_cntt_example_full
|
| 194 |
else: # Dùng đặc trưng quan trọng
|
| 195 |
sample_data_personal = sample_cntt_example_simple
|
| 196 |
name = st.text_input("👤 Họ và tên", value=sample_data_personal["name"])
|
| 197 |
-
|
| 198 |
-
|
| 199 |
elif sample_option == "Dùng ví dụ mẫu ngành Kinh tế":
|
| 200 |
# Check model type to load appropriate example data
|
| 201 |
if model_type == "Dùng toàn bộ đặc trưng":
|
| 202 |
sample_data_personal = sample_kinhte_example_full
|
| 203 |
else: # Dùng đặc trưng quan trọng
|
| 204 |
sample_data_personal = sample_kinhte_example_simple
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
else:
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
|
| 214 |
st.write("---")
|
| 215 |
|
| 216 |
# ===== Nhập thông tin học kỳ =====
|
| 217 |
def input_semester(semester_label, default_values=None):
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
else:
|
| 234 |
-
default_tc_qua_input = 0 # Or some default
|
| 235 |
-
sotc_qua = st.number_input("Số tín chỉ qua môn", 0, value=default_tc_qua_input, key=f"tcqua_{semester_label}")
|
| 236 |
-
with col2:
|
| 237 |
-
TCHK = st.number_input("Tổng tín chỉ học kỳ", 0, value=default_values[4] if default_values else 0, key=f"tchk_{semester_label}")
|
| 238 |
-
smhk = st.number_input("Số môn học kỳ", 0, value=default_values[5] if default_values else 0, key=f"smhk_{semester_label}")
|
| 239 |
-
mhpass = st.number_input("Số môn đạt", 0, value=default_values[6] if default_values else 0, key=f"mhpass_{semester_label}")
|
| 240 |
-
TBCHK = st.number_input("GPA", 0.0, 10.0, value=default_values[7] if default_values else 0.0, step=0.01, key=f"gpa_{semester_label}")
|
| 241 |
-
xep_loai_selected = st.selectbox("Xếp loại", list(range(7)), index=default_values[8] if default_values else 0, key=f"xeploai_{semester_label}")
|
| 242 |
-
return [somon0thi, sotc0thi, sotcno, mhno, TCHK, smhk, mhpass, TBCHK, xep_loai_selected, sotc_qua]
|
| 243 |
-
|
| 244 |
-
def input_important_features(semester_label, default_values=None):
|
| 245 |
-
with st.expander(f"📘 {semester_label}", expanded=True):
|
| 246 |
-
col1, col2, col3 = st.columns(3) # Use 3 columns for important features layout
|
| 247 |
-
with col1:
|
| 248 |
-
# Use consistent keys for input fields
|
| 249 |
-
sotc_qua = st.number_input("Số tín chỉ đạt", 0, value=default_values[0] if default_values and len(default_values) > 0 else 0, key=f"tcqua_imp_{semester_label}")
|
| 250 |
-
with col2:
|
| 251 |
-
sotcno = st.number_input("Số tín chỉ nợ", 0, value=default_values[1] if default_values and len(default_values) > 1 else 0, key=f"tcno_imp_{semester_label}")
|
| 252 |
-
with col3:
|
| 253 |
-
TBCHK = st.number_input("Điểm trung bình", 0.0, 10.0, value=default_values[2] if default_values and len(default_values) > 2 else 0.0, step=0.01, key=f"gpa_imp_{semester_label}")
|
| 254 |
-
return [sotc_qua, sotcno, TBCHK]
|
| 255 |
|
| 256 |
# ===== Giao diện theo mô hình =====
|
| 257 |
data = []
|
|
@@ -259,163 +222,46 @@ semesters = ["HỌC KỲ I", "HỌC KỲ II", "HỌC KỲ III", "HỌC KỲ IV",
|
|
| 259 |
|
| 260 |
# Mô hình 1: "Dùng toàn bộ dữ liệu"
|
| 261 |
if model_type == "Dùng toàn bộ đặc trưng":
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
example_data = sample_cntt_example_full if sample_option == "Dùng ví dụ mẫu ngành Công nghệ thông tin" else sample_kinhte_example_full if sample_option == "Dùng ví dụ mẫu ngành Kinh tế" else None
|
| 265 |
-
# Full model data has 9 features per semester + 1 extra (sotc_qua, which was calculated) = 10 features.
|
| 266 |
-
# Update: based on the data structure, there are 9 features per semester in the full model example.
|
| 267 |
-
# The input_semester function returns 10 values. Let's check the save_to_gsheet function's headers for full model.
|
| 268 |
-
# Headers for full model have 9 semester specific columns per semester.
|
| 269 |
-
# Let's correct input_semester to return 9 values matching the headers/example data.
|
| 270 |
-
# The example data has 9 values per semester for the full model.
|
| 271 |
-
# Let's fix the input_semester function to match the example data and headers (9 features).
|
| 272 |
-
# The example data has [0, 0, 0, 0, 17, 7, 7, 8.73, 0] -> 9 values.
|
| 273 |
-
# The input_semester returns [somon0thi, sotc0thi, sotcno, mhno, TCHK, smhk, mhpass, TBCHK, xep_loai_selected, sotc_qua] -> 10 values.
|
| 274 |
-
# The save_to_gsheet headers for full model list 9 columns per semester.
|
| 275 |
-
# Let's align input_semester to return the 9 values that correspond to the headers.
|
| 276 |
-
# Headers: Số môn không thi, Số tín chỉ không thi, Số tín chỉ nợ, Số môn không đạt, Số tín chỉ qua môn, Tổng tín chỉ học kỳ, Số môn học kỳ, Số môn đạt, GPA, Xếp loại
|
| 277 |
-
# This is 10 headers per semester. The example data has 9. Let's fix the example data structure to match headers or vice versa.
|
| 278 |
-
# Assuming headers are correct: Número môn không thi, Số tín chỉ không thi, Số tín chỉ nợ, Số môn không đạt, Số tín chỉ qua môn, Tổng tín chỉ học kỳ, Số môn học kỳ, Số môn đạt, GPA, Xếp loại (10 features)
|
| 279 |
-
# Let's update the example data for the full model to include all 10 features if possible or remove headers that don't match the example data.
|
| 280 |
-
# Given the original code's save_to_gsheet headers for the full model list 10 items per semester type, and the example data for full model has 9 items per semester type, there's a mismatch.
|
| 281 |
-
# Let's assume the `input_semester` function's return values are correct based on the intended model features, and the `sample_cntt_example_full` and `sample_kinhte_example_full` are slightly off or need adjustment to match the 10 inputs.
|
| 282 |
-
# The `input_semester` returns: somon0thi, sotc0thi, sotcno, mhno, TCHK, smhk, mhpass, TBCHK, xep_loai_selected, sotc_qua. This is 10 values.
|
| 283 |
-
# The `save_to_gsheet` headers for 'Trang tính1' list 10 headers per semester type. This matches.
|
| 284 |
-
# The `sample_cntt_example_full` and `sample_kinhte_example_full` have 9 values per semester. This is where the discrepancy is.
|
| 285 |
-
# To make the examples work correctly with the full model inputs, the example data structure should match the 10 features collected by `input_semester`.
|
| 286 |
-
# The missing feature in the example data seems to be `sotc_qua`.
|
| 287 |
-
# Let's adjust the example data to include a value for `sotc_qua` for each semester, or adjust the `input_semester` default value logic.
|
| 288 |
-
# A simpler fix for now is to adjust the `input_semester` default value logic to handle the case where example data might be shorter.
|
| 289 |
-
# The `default_tc_qua_input` line within `input_semester` was calculating it, let's remove that and rely solely on the example data if available.
|
| 290 |
-
# And if example data for `sotc_qua` is missing, default to 0.
|
| 291 |
-
|
| 292 |
-
# Corrected input_semester default value handling:
|
| 293 |
-
def input_semester_corrected(semester_label, default_values=None):
|
| 294 |
-
with st.expander(f"📖 {semester_label}", expanded=True):
|
| 295 |
-
col1, col2 = st.columns(2)
|
| 296 |
-
with col1:
|
| 297 |
-
somon0thi = st.number_input("Số môn không thi", 0, value=default_values[0] if default_values and len(default_values) > 0 else 0, key=f"sm0_{semester_label}")
|
| 298 |
-
sotc0thi = st.number_input("Số tín chỉ không thi", 0, value=default_values[1] if default_values and len(default_values) > 1 else 0, key=f"tc0_{semester_label}")
|
| 299 |
-
sotcno = st.number_input("Số tín chỉ nợ", 0, value=default_values[2] if default_values and len(default_values) > 2 else 0, key=f"tcno_{semester_label}")
|
| 300 |
-
mhno = st.number_input("Số môn không đạt", 0, value=default_values[3] if default_values and len(default_values) > 3 else 0, key=f"mhno_{semester_label}")
|
| 301 |
-
# Assuming sotc_qua is the 5th value in the example data if 9 values exist
|
| 302 |
-
sotc_qua_default = default_values[4] if default_values and len(default_values) > 4 else 0
|
| 303 |
-
sotc_qua = st.number_input("Số tín chỉ qua môn", 0, value=sotc_qua_default, key=f"tcqua_{semester_label}")
|
| 304 |
-
with col2:
|
| 305 |
-
TCHK = st.number_input("Tổng tín chỉ học kỳ", 0, value=default_values[5] if default_values and len(default_values) > 5 else 0, key=f"tchk_{semester_label}")
|
| 306 |
-
smhk = st.number_input("Số môn học kỳ", 0, value=default_values[6] if default_values and len(default_values) > 6 else 0, key=f"smhk_{semester_label}")
|
| 307 |
-
mhpass = st.number_input("Số môn đạt", 0, value=default_values[7] if default_values and len(default_values) > 7 else 0, key=f"mhpass_{semester_label}")
|
| 308 |
-
TBCHK = st.number_input("GPA", 0.0, 10.0, value=default_values[8] if default_values and len(default_values) > 8 else 0.0, step=0.01, key=f"gpa_{semester_label}")
|
| 309 |
-
xep_loai_selected = st.selectbox("Xếp loại", list(range(7)), index=default_values[9] if default_values and len(default_values) > 9 else 0, key=f"xeploai_{semester_label}") # Assuming Xếp loại is the 10th value
|
| 310 |
-
return [somon0thi, sotc0thi, sotcno, mhno, sotc_qua, TCHK, smhk, mhpass, TBCHK, xep_loai_selected] # Return 10 values
|
| 311 |
-
|
| 312 |
-
# Let's update the example data structure to have 10 values per semester for consistency
|
| 313 |
-
# Based on the headers, the order should be: Số môn không thi, Số tín chỉ không thi, Số tín chỉ nợ, Số môn không đạt, Số tín chỉ qua môn, Tổng tín chỉ học kỳ, Số môn học kỳ, Số môn đạt, GPA, Xếp loại
|
| 314 |
-
# The original example data was [somon0thi, sotc0thi, sotcno, mhno, TCHK, smhk, mhpass, TBCHK, xep_loai_selected] -> 9 values
|
| 315 |
-
# It seems "Số tín chỉ qua môn" was missing or intended to be calculated.
|
| 316 |
-
# Let's assume "Số tín chỉ qua môn" is implicitly TCHK - sotcno.
|
| 317 |
-
# However, the headers list "Số tín chỉ qua môn" as a separate column.
|
| 318 |
-
# Let's assume the headers are correct and the example data needs to match the headers.
|
| 319 |
-
# Let's restructure example data to have 10 values, adding a plausible value for "Số tín chỉ qua môn" (TCHK - sotcno).
|
| 320 |
-
|
| 321 |
-
sample_cntt_example_full = {
|
| 322 |
-
"name": "Nguyễn Văn A",
|
| 323 |
-
"student_id": "10117367",
|
| 324 |
-
"major": "Công nghệ thông tin",
|
| 325 |
-
"semester_data": [
|
| 326 |
-
# HK1: sm0, tc0, tcno, mhno, tc_qua (calculated: TCHK-tcno), TCHK, smhk, mhpass, GPA, xeploai
|
| 327 |
-
0, 0, 0, 0, 17-0, 17, 7, 7, 8.73, 0,
|
| 328 |
-
# HK2:
|
| 329 |
-
0, 0, 0, 0, 17-0, 17, 7, 7, 8.19, 0,
|
| 330 |
-
# HK3:
|
| 331 |
-
0, 0, 0, 0, 17-0, 17, 7, 7, 7.90, 0,
|
| 332 |
-
# HK4:
|
| 333 |
-
0, 0, 0, 0, 17-0, 17, 7, 7, 8.19, 0,
|
| 334 |
-
# HK5:
|
| 335 |
-
0, 0, 0, 0, 19-0, 19, 7, 6, 8.18, 0,
|
| 336 |
-
# HK6: tcno=5, mhno=2, TCHK=19, mhpass=6, xeploai=1. Assuming tc_qua = TCHK - tcno = 19-5 = 14
|
| 337 |
-
0, 0, 5, 2, 19-5, 19, 7, 6, 7.10, 1
|
| 338 |
-
]
|
| 339 |
-
}
|
| 340 |
-
|
| 341 |
-
sample_kinhte_example_full = {
|
| 342 |
-
"name": "Trần Thị C",
|
| 343 |
-
"student_id": "11418093",
|
| 344 |
-
"major": "Kinh tế",
|
| 345 |
-
"semester_data": [
|
| 346 |
-
# HK1:
|
| 347 |
-
0, 0, 0, 0, 16-0, 16, 6, 6, 7.00, 1,
|
| 348 |
-
# HK2:
|
| 349 |
-
0, 0, 0, 0, 18-0, 18, 7, 7, 8.20, 1,
|
| 350 |
-
# HK3:
|
| 351 |
-
0, 0, 0, 0, 17-0, 17, 7, 7, 7.80, 1,
|
| 352 |
-
# HK4:
|
| 353 |
-
0, 0, 0, 0, 17-0, 17, 7, 6, 7.90, 1,
|
| 354 |
-
# HK5:
|
| 355 |
-
0, 0, 0, 0, 19-0, 19, 8, 5, 8.10, 0,
|
| 356 |
-
# HK6: tcno=8, mhno=2, TCHK=19, mhpass=6, xeploai=1. Assuming tc_qua = TCHK - tcno = 19-8 = 11
|
| 357 |
-
0, 0, 8, 2, 19-8, 19, 7, 6, 7.30, 1
|
| 358 |
-
]
|
| 359 |
-
}
|
| 360 |
-
# Now the example data for the full model should match the 10 inputs per semester.
|
| 361 |
-
|
| 362 |
example_data = sample_cntt_example_full if sample_option == "Dùng ví dụ mẫu ngành Công nghệ thông tin" else sample_kinhte_example_full if sample_option == "Dùng ví dụ mẫu ngành Kinh tế" else None
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
|
| 368 |
|
| 369 |
# Mô hình 2: "Dùng đặc trưng quan trọng"
|
| 370 |
else: # model_type == "Dùng đặc trưng quan trọng"
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
example_data = sample_cntt_example_simple if sample_option == "Dùng ví dụ mẫu ngành Công nghệ thông tin" else sample_kinhte_example_simple if sample_option == "Dùng ví dụ mẫu ngành Kinh tế" else None
|
| 376 |
-
# Slice 3 values per semester for the simple model example data
|
| 377 |
-
default_values = example_data["semester_data"][idx*3:(idx+1)*3] if example_data else None
|
| 378 |
-
|
| 379 |
-
# Use expander to group each semester's fields
|
| 380 |
with st.expander(f"📘 {sem}", expanded=True):
|
| 381 |
-
# Use 3 columns to align with the image layout for all semesters
|
| 382 |
col1, col2, col3 = st.columns(3)
|
| 383 |
with col1:
|
| 384 |
-
|
| 385 |
-
sotc_qua = st.number_input("Số tín chỉ đạt", 0, value=default_values[0] if default_values and len(default_values) > 0 else 0, key=f"tcqua_imp_{sem}")
|
| 386 |
with col2:
|
| 387 |
-
sotcno = st.number_input("Số tín chỉ nợ", 0, value=default_values[1] if default_values
|
| 388 |
with col3:
|
| 389 |
-
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
# Collect semester data (3 values per semester)
|
| 393 |
-
data += [sotc_qua, sotcno, TBCHK]
|
| 394 |
-
|
| 395 |
-
final_features = np.array(data).reshape(1, -1) # Should be 6 semesters * 3 features = 18 features
|
| 396 |
|
| 397 |
# ===== Predict =====
|
| 398 |
if st.button("🎯 DỰ BÁO"):
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
|
| 403 |
-
else: # model_type == "Dùng đặc trưng quan trọng"
|
| 404 |
-
prediction = model_important.predict(final_features)
|
| 405 |
-
sheet_name = "Trang tính2" # For important data model, save to Trang tính2
|
| 406 |
-
|
| 407 |
|
| 408 |
-
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
else:
|
| 412 |
-
st.error(f"⚠️ Bạn {name} - {major} cần cố gắng hơn! Có nguy cơ trễ hạn.")
|
| 413 |
-
st.snow()
|
| 414 |
|
| 415 |
-
|
| 416 |
-
|
| 417 |
-
|
| 418 |
-
|
|
|
|
|
|
|
| 419 |
|
| 420 |
-
|
| 421 |
-
save_to_gsheet(name, student_id, major, prediction, data,sheet_name) # Pass data to save function
|
|
|
|
| 15 |
|
| 16 |
# ===== Custom CSS =====
|
| 17 |
st.markdown("""
|
| 18 |
+
<style>
|
| 19 |
+
html, body, [class*="css"] {
|
| 20 |
+
font-family: 'Poppins', sans-serif;
|
| 21 |
+
}
|
| 22 |
+
.block-container {
|
| 23 |
+
padding: 2rem 2rem 2rem 2rem;
|
| 24 |
+
}
|
| 25 |
+
.stButton button {
|
| 26 |
+
background: linear-gradient(90deg, #4F8BF9 0%, #8E2DE2 100%);
|
| 27 |
+
border: none;
|
| 28 |
+
padding: 0.75em 1.5em;
|
| 29 |
+
border-radius: 8px;
|
| 30 |
+
color: white;
|
| 31 |
+
font-size: 18px;
|
| 32 |
+
font-weight: bold;
|
| 33 |
+
}
|
| 34 |
+
</style>
|
| 35 |
""", unsafe_allow_html=True)
|
| 36 |
|
| 37 |
# ===== GSheet integration =====
|
|
|
|
| 44 |
|
| 45 |
# ===== Save to Google Sheets =====
|
| 46 |
def save_to_gsheet(name, student_id, major, prediction, semester_data, sheet_name="Trang tính1"):
|
| 47 |
+
# Get the current date and time
|
| 48 |
+
now = datetime.now()
|
| 49 |
+
current_time = now.strftime("%Y-%m-%d %H:%M:%S")
|
| 50 |
+
|
| 51 |
+
# Ensure prediction is in the expected format (0 or 1)
|
| 52 |
+
if isinstance(prediction, np.ndarray):
|
| 53 |
+
prediction = int(prediction[0]) # Convert to int if it's ndarray
|
| 54 |
+
|
| 55 |
+
elif isinstance(prediction, np.int64):
|
| 56 |
+
prediction = int(prediction) # Convert np.int64 to int
|
| 57 |
+
|
| 58 |
+
# Flatten semester data to individual columns for each semester
|
| 59 |
+
semester_data_flat = [str(val) for val in semester_data] # Convert all semester data values to strings
|
| 60 |
+
|
| 61 |
+
# Create a data row to insert into the sheet
|
| 62 |
+
data_row = [name, student_id, major, prediction, current_time] + semester_data_flat
|
| 63 |
+
|
| 64 |
+
# Connect to Google Sheets
|
| 65 |
+
client = get_gsheet_client()
|
| 66 |
+
|
| 67 |
+
# Use the Spreadsheet ID (replace with your actual ID)
|
| 68 |
+
sheet_id = "1i7bDNvLVLXN93_e-FN0JLzpg1jb64Z_aEuyPjIfwbdQ" # Use your actual ID here
|
| 69 |
+
sheet = client.open_by_key(sheet_id).worksheet(sheet_name)
|
| 70 |
+
|
| 71 |
+
# Get all values from the sheet to check if the first row is empty (i.e., headers not created yet)
|
| 72 |
+
all_values = sheet.get_all_values()
|
| 73 |
|
| 74 |
# Define headers based on the model type for accurate sheet saving
|
| 75 |
+
if sheet_name == "Trang tính1": # Full features model
|
| 76 |
headers = ["Họ và tên", "MSV", "Khoa", "Dự báo", "Thời gian"] + [
|
| 77 |
f"Số môn không thi - HK{i+1}" for i in range(6)] + [
|
| 78 |
f"Số tín chỉ không thi - HK{i+1}" for i in range(6)] + [
|
|
|
|
| 85 |
f"GPA - HK{i+1}" for i in range(6)] + [
|
| 86 |
f"Xếp loại - HK{i+1}" for i in range(6)]
|
| 87 |
|
| 88 |
+
elif sheet_name == "Trang tính2": # Important features model
|
| 89 |
+
headers = ["Họ và tên", "MSV", "Khoa", "Dự báo", "Thời gian"] + [
|
| 90 |
f"Số tín chỉ đạt - HK{i+1}" for i in range(6)] + [
|
| 91 |
f"Số tín chỉ nợ - HK{i+1}" for i in range(6)] + [
|
| 92 |
f"Điểm trung bình - HK{i+1}" for i in range(6)]
|
| 93 |
|
| 94 |
# If the sheet is empty or if headers are missing, insert headers
|
| 95 |
+
if not all_values or len(all_values[0]) != len(headers): # Ensure number of columns matches expected headers
|
| 96 |
+
sheet.append_row(headers) # Create headers if missing
|
| 97 |
+
|
| 98 |
+
# Append data to the sheet
|
| 99 |
+
sheet.append_row(data_row)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 100 |
|
| 101 |
# ===== HEADER =====
|
| 102 |
st.markdown("<h1 style='text-align: center; color: #003366;'>🎓 DỰ ĐOÁN KHẢ NĂNG TỐT NGHIỆP ĐÚNG HẠN</h1>", unsafe_allow_html=True)
|
|
|
|
| 111 |
|
| 112 |
sample_option = st.selectbox("📝 Chọn ví dụ:", ["Không ví dụ", "Dùng ví dụ mẫu ngành Công nghệ thông tin", "Dùng ví dụ mẫu ngành Kinh tế"])
|
| 113 |
|
| 114 |
+
# Ví dụ mẫu cho mô hình "Dùng toàn bộ dữ liệu"
|
| 115 |
sample_cntt_example_full = {
|
| 116 |
+
"name": "Nguyễn Văn A",
|
| 117 |
+
"student_id": "10117367",
|
| 118 |
+
"major": "Công nghệ thông tin",
|
| 119 |
+
"semester_data": [
|
| 120 |
+
0, 0, 0, 0, 17, 7, 7, 8.73, 0,
|
| 121 |
+
0, 0, 0, 0, 17, 7, 7, 8.19, 0,
|
| 122 |
+
0, 0, 0, 0, 17, 7, 7, 7.90, 0,
|
| 123 |
+
0, 0, 0, 0, 17, 7, 7, 8.19, 0,
|
| 124 |
+
0, 0, 0, 0, 19, 7, 6, 8.18, 0,
|
| 125 |
+
0, 0, 5, 2, 19, 7, 5, 7.10, 1
|
| 126 |
+
]
|
| 127 |
}
|
| 128 |
|
| 129 |
+
# Ví dụ mẫu cho mô hình "Dùng mô hình đơn giản"
|
|
|
|
| 130 |
sample_kinhte_example_simple = {
|
| 131 |
+
"name": "Trần Thị B",
|
| 132 |
+
"student_id": "11418092",
|
| 133 |
+
"major": "Kinh tế",
|
| 134 |
+
"semester_data": [
|
| 135 |
+
17, 7, 7.64, # Học kỳ 1: Số tín chỉ đạt, Số tín chỉ nợ, GPA
|
| 136 |
+
17, 7, 7.19, # Học kỳ 2: Số tín chỉ đạt, Số tín chỉ nợ, GPA
|
| 137 |
+
17, 7, 6.94, # Học kỳ 3: Số tín chỉ đạt, Số tín chỉ nợ, GPA
|
| 138 |
+
17, 7, 7.19, # Học kỳ 4: Số tín chỉ đạt, Số tín chỉ nợ, GPA
|
| 139 |
+
19, 7, 7.65, # Học kỳ 5: Số tín chỉ đạt, Số tín chỉ nợ, GPA
|
| 140 |
+
19, 7, 7.49 # Học kỳ 6: Số tín chỉ đạt, Số tín chỉ nợ, GPA
|
| 141 |
+
]
|
| 142 |
}
|
| 143 |
|
| 144 |
|
|
|
|
| 145 |
# ===== Ví dụ cho mô hình "Dùng mô hình đơn giản" cho CNTT =====
|
| 146 |
sample_cntt_example_simple = {
|
| 147 |
+
"name": "Nguyễn Văn B",
|
| 148 |
+
"student_id": "10117368",
|
| 149 |
+
"major": "Công nghệ thông tin",
|
| 150 |
+
"semester_data": [
|
| 151 |
+
15, 8, 7.50, # Học kỳ 1: Số tín chỉ đạt, Số tín chỉ nợ, GPA
|
| 152 |
+
17, 7, 8.10, # Học kỳ 2: Số tín chỉ đạt, Số tín chỉ nợ, GPA
|
| 153 |
+
18, 7, 7.90, # Học kỳ 3: Số tín chỉ đạt, Số tín chỉ nợ, GPA
|
| 154 |
+
17, 7, 7.80, # Học kỳ 4: Số tín chỉ đạt, Số tín chỉ nợ, GPA
|
| 155 |
+
19, 8, 8.30, # Học kỳ 5: Số tín chỉ đạt, Số tín chỉ nợ, GPA
|
| 156 |
+
19, 7, 7.20 # Học kỳ 6: Số tín chỉ đạt, Số tín chỉ nợ, GPA
|
| 157 |
+
]
|
| 158 |
}
|
| 159 |
|
| 160 |
# ===== Ví dụ cho mô hình "Dùng mô hình đơn giản" cho Kinh tế =====
|
| 161 |
sample_kinhte_example_full = {
|
| 162 |
+
"name": "Trần Thị C",
|
| 163 |
+
"student_id": "11418093",
|
| 164 |
+
"major": "Kinh tế",
|
| 165 |
+
"semester_data": [
|
| 166 |
+
0, 0, 0, 0, 16, 6, 6, 7.00, 1,
|
| 167 |
+
0, 0, 0, 0, 18, 7, 7, 8.20, 1,
|
| 168 |
+
0, 0, 0, 0, 17, 7, 7, 7.80, 1,
|
| 169 |
+
0, 0, 0, 0, 17, 7, 6, 7.90, 1,
|
| 170 |
+
0, 0, 0, 0, 19, 8, 5, 8.10, 0,
|
| 171 |
+
0, 0, 8, 2, 19, 7, 6, 7.30, 1
|
| 172 |
+
]
|
| 173 |
}
|
| 174 |
|
| 175 |
# ===== Thông tin cá nhân =====
|
| 176 |
if sample_option == "Dùng ví dụ mẫu ngành Công nghệ thông tin":
|
| 177 |
+
# Check model type to load appropriate example data
|
| 178 |
if model_type == "Dùng toàn bộ đặc trưng":
|
| 179 |
sample_data_personal = sample_cntt_example_full
|
| 180 |
else: # Dùng đặc trưng quan trọng
|
| 181 |
sample_data_personal = sample_cntt_example_simple
|
| 182 |
name = st.text_input("👤 Họ và tên", value=sample_data_personal["name"])
|
| 183 |
+
student_id = st.text_input("🎓 Mã sinh viên", value=sample_data_personal["student_id"])
|
| 184 |
+
major = st.selectbox("📚 Ngành học", ["Công nghệ thông tin", "Kinh tế"], index=0)
|
| 185 |
elif sample_option == "Dùng ví dụ mẫu ngành Kinh tế":
|
| 186 |
# Check model type to load appropriate example data
|
| 187 |
if model_type == "Dùng toàn bộ đặc trưng":
|
| 188 |
sample_data_personal = sample_kinhte_example_full
|
| 189 |
else: # Dùng đặc trưng quan trọng
|
| 190 |
sample_data_personal = sample_kinhte_example_simple
|
| 191 |
+
name = st.text_input("👤 Họ và tên", value=sample_data_personal["name"])
|
| 192 |
+
student_id = st.text_input("🎓 Mã sinh viên", value=sample_data_personal["student_id"])
|
| 193 |
+
major = st.selectbox("📚 Ngành học", ["Công nghệ thông tin", "Kinh tế"], index=1)
|
| 194 |
else:
|
| 195 |
+
name = st.text_input("👤 Họ và tên")
|
| 196 |
+
student_id = st.text_input("🎓 Mã sinh viên")
|
| 197 |
+
major = st.selectbox("📚 Ngành học", ["Công nghệ thông tin", "Kinh tế"])
|
|
|
|
| 198 |
|
| 199 |
st.write("---")
|
| 200 |
|
| 201 |
# ===== Nhập thông tin học kỳ =====
|
| 202 |
def input_semester(semester_label, default_values=None):
|
| 203 |
+
with st.expander(f"📖 {semester_label}", expanded=True):
|
| 204 |
+
col1, col2 = st.columns(2)
|
| 205 |
+
with col1:
|
| 206 |
+
somon0thi = st.number_input("Số môn không thi", 0, value=default_values[0] if default_values else 0, key=f"sm0_{semester_label}")
|
| 207 |
+
sotc0thi = st.number_input("Số tín chỉ không thi", 0, value=default_values[1] if default_values else 0, key=f"tc0_{semester_label}")
|
| 208 |
+
sotcno = st.number_input("Số tín chỉ nợ", 0, value=default_values[2] if default_values else 0, key=f"tcno_{semester_label}")
|
| 209 |
+
mhno = st.number_input("Số môn không đạt", 0, value=default_values[3] if default_values else 0, key=f"mhno_{semester_label}")
|
| 210 |
+
sotc_qua = st.number_input("Số tín chỉ qua môn", 0, value=default_values[4] if default_values else 0, key=f"tcqua_{semester_label}")
|
| 211 |
+
with col2:
|
| 212 |
+
TCHK = st.number_input("Tổng tín chỉ học kỳ", 0, value=default_values[5] if default_values else 0, key=f"tchk_{semester_label}")
|
| 213 |
+
smhk = st.number_input("Số môn học kỳ", 0, value=default_values[6] if default_values else 0, key=f"smhk_{semester_label}")
|
| 214 |
+
mhpass = st.number_input("Số môn đạt", 0, value=default_values[7] if default_values else 0, key=f"mhpass_{semester_label}")
|
| 215 |
+
TBCHK = st.number_input("GPA", 0.0, 10.0, value=default_values[8] if default_values else 0.0, step=0.01, key=f"gpa_{semester_label}")
|
| 216 |
+
xep_loai_selected = st.selectbox("Xếp loại", list(range(7)), index=default_values[9] if default_values else 0, key=f"xeploai_{semester_label}")
|
| 217 |
+
return [somon0thi, sotc0thi, sotcno, mhno, sotc_qua, TCHK, smhk, mhpass, TBCHK, xep_loai_selected]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 218 |
|
| 219 |
# ===== Giao diện theo mô hình =====
|
| 220 |
data = []
|
|
|
|
| 222 |
|
| 223 |
# Mô hình 1: "Dùng toàn bộ dữ liệu"
|
| 224 |
if model_type == "Dùng toàn bộ đặc trưng":
|
| 225 |
+
st.subheader("🔢 Nhập thông tin học kỳ chi tiết")
|
| 226 |
+
for idx, sem in enumerate(semesters):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 227 |
example_data = sample_cntt_example_full if sample_option == "Dùng ví dụ mẫu ngành Công nghệ thông tin" else sample_kinhte_example_full if sample_option == "Dùng ví dụ mẫu ngành Kinh tế" else None
|
| 228 |
+
default_values = example_data["semester_data"][idx*10:(idx+1)*10] if example_data else None
|
| 229 |
+
data += input_semester(sem, default_values) # Use the corrected function
|
| 230 |
+
nganh = 0 if major == "Công nghệ thông tin" else 1
|
| 231 |
+
final_features = np.array(data + [nganh]).reshape(1, -1)
|
|
|
|
| 232 |
|
| 233 |
# Mô hình 2: "Dùng đặc trưng quan trọng"
|
| 234 |
else: # model_type == "Dùng đặc trưng quan trọng"
|
| 235 |
+
st.subheader("✨ Nhập thông tin rút gọn")
|
| 236 |
+
for idx, sem in enumerate(semesters):
|
| 237 |
+
example_data = sample_cntt_example_simple if sample_option == "Dùng ví dụ mẫu ngành Công nghệ thông tin" else sample_kinhte_example_simple if sample_option == "Dùng ví dụ mẫu ngành Kinh tế" else None
|
| 238 |
+
default_values = example_data["semester_data"][idx*3:(idx+1)*3] if example_data else None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 239 |
with st.expander(f"📘 {sem}", expanded=True):
|
|
|
|
| 240 |
col1, col2, col3 = st.columns(3)
|
| 241 |
with col1:
|
| 242 |
+
sotc_qua = st.number_input("Số tín chỉ đạt", 0, value=default_values[0] if default_values else 0, key=f"tcqua_imp_{sem}")
|
|
|
|
| 243 |
with col2:
|
| 244 |
+
sotcno = st.number_input("Số tín chỉ nợ", 0, value=default_values[1] if default_values else 0, key=f"tcno_imp_{sem}")
|
| 245 |
with col3:
|
| 246 |
+
TBCHK = st.number_input("Điểm trung bình hệ 10", 0.0, 10.0, value=default_values[2] if default_values else 0.0, step=0.01, key=f"gpa_imp_{sem}")
|
| 247 |
+
data += [sotc_qua, sotcno, TBCHK]
|
| 248 |
+
final_features = np.array(data).reshape(1, -1)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 249 |
|
| 250 |
# ===== Predict =====
|
| 251 |
if st.button("🎯 DỰ BÁO"):
|
| 252 |
+
if model_type == "Dùng toàn bộ đặc trưng":
|
| 253 |
+
prediction = model_full.predict(final_features)
|
| 254 |
+
sheet_name = "Trang tính1" # For full data model, save to Trang tính1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 255 |
|
| 256 |
+
else: # model_type == "Dùng đặc trưng quan trọng"
|
| 257 |
+
prediction = model_important.predict(final_features)
|
| 258 |
+
sheet_name = "Trang tính2" # For important data model, save to Trang tính2
|
|
|
|
|
|
|
|
|
|
| 259 |
|
| 260 |
+
if prediction[0] == 1:
|
| 261 |
+
st.success(f"🎉 Chúc mừng bạn {name} - {major}! Bạn có khả năng tốt nghiệp đúng hạn!")
|
| 262 |
+
st.balloons()
|
| 263 |
+
else:
|
| 264 |
+
st.error(f"⚠️ Bạn {name} - {major} cần cố gắng hơn! Có nguy cơ trễ hạn.")
|
| 265 |
+
st.snow()
|
| 266 |
|
| 267 |
+
save_to_gsheet(name, student_id, major, prediction, data, sheet_name) # Pass data to save function
|
|
|