namngo commited on
Commit
3d6329e
·
verified ·
1 Parent(s): af59f5c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +260 -319
app.py CHANGED
@@ -7,168 +7,118 @@ from datetime import datetime
7
  import json
8
 
9
  # ===== Load models =====
10
- # Ensure these file paths are correct relative to your script
11
- try:
12
- model_full = joblib.load("random_forest_model_full.pkl")
13
- model_important = joblib.load("random_forest_model_importainfeature.pkl")
14
- except FileNotFoundError:
15
- st.error("Error loading model files. Make sure 'random_forest_model_full.pkl' and 'random_forest_model_importainfeature.pkl' are in the same directory.")
16
- st.stop()
17
-
18
 
19
  # ===== Setup page =====
20
  st.set_page_config(page_title="Dự đoán tốt nghiệp đúng hạn", page_icon="🎓", layout="wide")
21
 
22
  # ===== Custom CSS =====
23
  st.markdown("""
24
- <style>
25
- html, body, [class*="css"] {
26
- font-family: 'Arial', sans-serif; /* Changed font for better compatibility */
27
- }
28
- .block-container {
29
- padding: 2rem 2rem 2rem 2rem;
30
- }
31
- .stButton button {
32
- background: linear-gradient(90deg, #4F8BF9 0%, #8E2DE2 100%);
33
- border: none;
34
- padding: 0.75em 1.5em;
35
- border-radius: 8px;
36
- color: white;
37
- font-size: 18px;
38
- font-weight: bold;
39
- }
40
-
41
- /* Make number_input, selectbox, and text_input shorter using data-testid */
42
- div[data-testid="stNumberInput"] label {
43
- font-weight: bold; /* Make labels bold */
44
- }
45
- div[data-testid="stSelectbox"] label {
46
- font-weight: bold; /* Make labels bold */
47
- }
48
- div[data-testid="stTextInput"] label {
49
- font-weight: bold; /* Make labels bold */
50
- }
51
-
52
-
53
- /* Style the expander header */
54
- .streamlit-expander {
55
- border: 1px solid #ccc; /* Add border */
56
- border-radius: 5px; /* Add rounded corners */
57
- margin-bottom: 10px; /* Add space below */
58
- }
59
-
60
- .streamlit-expander > div:first-child {
61
- background-color: #f0f2f6; /* Light background color for header */
62
- padding: 10px; /* Add padding */
63
- border-radius: 5px 5px 0 0; /* Rounded top corners */
64
- }
65
-
66
- /* Style the expander content */
67
- .streamlit-expander > div:last-child {
68
- padding: 10px; /* Add padding to content */
69
- }
70
-
71
- /* Adjust width for inputs inside expanders */
72
- .streamlit-expander div[data-testid="stNumberInput"],
73
- .streamlit-expander div[data-testid="stSelectbox"],
74
- .streamlit-expander div[data-testid="stTextInput"] {
75
- max-width: 95%; /* Limit width within expander */
76
- }
77
-
78
-
79
- /* Ensure inputs inside columns don't overflow */
80
- div[data-testid="stNumberInput"] input[type="number"],
81
- div[data-testid="stSelectbox"] select,
82
- div[data-testid="stTextInput"] input[type="text"] {
83
- width: 100%; /* Make input elements fill their container */
84
- }
85
-
86
- </style>
87
  """, unsafe_allow_html=True)
88
 
89
  # ===== GSheet integration =====
90
  @st.cache_resource
91
  def get_gsheet_client():
92
- try:
93
- scope = ["https://spreadsheets.google.com/feeds", "https://www.googleapis.com/auth/drive"]
94
- creds = ServiceAccountCredentials.from_json_keyfile_name("credentials.json", scope)
95
- client = gspread.authorize(creds)
96
- return client
97
- except FileNotFoundError:
98
- st.error("Error loading Google Sheets credentials. Make sure 'credentials.json' is in the same directory.")
99
- st.stop()
100
- except Exception as e:
101
- st.error(f"Error connecting to Google Sheets: {e}")
102
- st.stop()
103
 
104
  # ===== Save to Google Sheets =====
105
  def save_to_gsheet(name, student_id, major, prediction, semester_data, sheet_name="Trang tính1"):
106
- # Get the current date and time
107
- now = datetime.now()
108
- current_time = now.strftime("%Y-%m-%d %H:%M:%S")
109
-
110
- # Ensure prediction is in the expected format (0 or 1)
111
- if isinstance(prediction, np.ndarray):
112
- prediction = int(prediction[0]) # Convert to int if it's ndarray
113
-
114
- elif isinstance(prediction, np.int64):
115
- prediction = int(prediction) # Convert np.int64 to int
116
-
117
- # Flatten semester data to individual columns for each semester
118
- semester_data_flat = [str(val) for val in semester_data] # Convert all semester data values to strings
119
-
120
- # Create a data row to insert into the sheet
121
- data_row = [name, student_id, major, prediction, current_time] + semester_data_flat
122
-
123
- # Connect to Google Sheets
124
- client = get_gsheet_client()
125
-
126
- # Use the Spreadsheet ID (replace with your actual ID)
127
- sheet_id = "1i7bDNvLVLXN93_e-FN0JLzpg1jb64Z_aEuyPjIfwbdQ" # Use your actual ID here
128
- try:
129
- sheet = client.open_by_key(sheet_id).worksheet(sheet_name)
130
- except gspread.WorksheetNotFound:
131
- st.error(f"Google Sheet '{sheet_name}' not found. Please create a sheet with this name.")
132
- return
133
- except Exception as e:
134
- st.error(f"Error accessing Google Sheet: {e}")
135
- return
136
-
137
-
138
- # Get all values from the sheet to check if the first row is empty (i.e., headers not created yet)
139
- all_values = sheet.get_all_values()
140
-
141
- # Define headers based on the sheet name (model type)
142
- if sheet_name == "Trang tính1": # Full model headers
143
- headers = ["Họ tên", "MSV", "Khoa", "Dự báo", "Thời gian"] + [
144
- f"Số môn không thi - HK{i+1}" for i in range(6)] + [
145
- f"Số tín chỉ không thi - HK{i+1}" for i in range(6)] + [
146
- f"Số tín chỉ nợ - HK{i+1}" for i in range(6)] + [
147
- f"Số môn không đạt - HK{i+1}" for i in range(6)] + [
148
- f"Số tín chỉ qua môn - HK{i+1}" for i in range(6)] + [
149
- f"Tổng tín chỉ học kỳ - HK{i+1}" for i in range(6)] + [
150
- f"Số môn học kỳ - HK{i+1}" for i in range(6)] + [
151
- f"Số môn đạt - HK{i+1}" for i in range(6)] + [
152
- f"GPA - HK{i+1}" for i in range(6)] + [
153
- f"Xếp loại - HK{i+1}" for i in range(6)]
154
- elif sheet_name == "Trang tính2": # Important features model headers
155
- headers = ["Họ và tên", "MSV", "Khoa", "Dự báo", "Thời gian"] + [
156
- f"Số tín chỉ đạt - HK{i+1}" for i in range(6)] + [
157
- f"Số tín chỉ không đạt - HK{i+1}" for i in range(6)] + [
158
- f"Điểm trung bình hệ 10 - HK{i+1}" for i in range(6)]
159
- else: # Default headers if sheet_name is unexpected
160
- headers = ["Họ và tên", "MSV", "Khoa", "Dự báo", "Thời gian"] + [f"Data{i+1}" for i in range(len(semester_data_flat))]
161
-
162
-
163
- # If the sheet is empty or if headers are missing, insert headers
164
- if not all_values or all_values[0] != headers: # Check if the first row matches the expected headers
165
- sheet.append_row(headers) # Create headers if missing or incorrect
166
-
167
- # Append data to the sheet
168
- try:
169
- sheet.append_row(data_row)
170
- except Exception as e:
171
- st.error(f"Error appending data to Google Sheet: {e}")
 
 
 
 
 
 
 
 
 
172
 
173
 
174
  # ===== HEADER =====
@@ -182,210 +132,201 @@ model_type = st.sidebar.selectbox("🧠 Chọn mô hình dự báo:", ["Dùng to
182
  # ===== Giao diện nhập ví dụ =====
183
  st.subheader("🔢 Chọn ví dụ mẫu hoặc nhập thông tin cá nhân")
184
 
185
- # Update sample options based on selected model type
186
- if model_type == "Dùng toàn bộ đặc trưng":
187
- 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ế"])
188
- else: # "Dùng đặc trưng quan trọng"
189
- 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ế"]) # Keep options for consistency
190
-
191
 
192
  # ===== Ví dụ mẫu cho mô hình "Dùng toàn bộ dữ liệu" =====
193
  sample_cntt_example_full = {
194
- "name": "Nguyễn Văn A",
195
- "student_id": "10117367",
196
- "major": "Công nghệ thông tin",
197
- "semester_data": [
198
- 0, 0, 0, 0, 17, 7, 7, 8.73, 0, # HK1: sm0, tc0, tcno, mhno, TCHK, smhk, mhpass, TBCHK, xeploai, sotc_qua (this last one was added in the code logic, need to check the original data structure)
199
- 0, 0, 0, 0, 17, 7, 7, 8.19, 0, # HK2
200
- 0, 0, 0, 0, 17, 7, 7, 7.90, 0, # HK3
201
- 0, 0, 0, 0, 17, 7, 7, 8.19, 0, # HK4
202
- 0, 0, 0, 0, 19, 7, 6, 8.18, 0, # HK5
203
- 0, 0, 5, 2, 19, 7, 5, 7.10, 1 # HK6
204
- ]
205
  }
206
 
207
- # Correcting sample data structure for the full model (input_semester returns 10 values)
208
- # Let's re-examine the input_semester function's return:
209
- # return [somon0thi, sotc0thi, sotcno, mhno, TCHK, smhk, mhpass, TBCHK, xep_loai_selected, sotc_qua] - This is 10 values.
210
- # The sample data has 9 values per semester. This is a mismatch.
211
- # Let's adjust the sample data to match the 10 expected values per semester by adding a placeholder (e.g., 0) for 'sotc_qua' in the sample data, assuming it was derived or not a direct input feature in the original data.
212
-
213
- sample_cntt_example_full = {
214
- "name": "Nguyễn Văn A",
215
- "student_id": "10117367",
216
- "major": "Công nghệ thông tin",
217
- "semester_data": [
218
- 0, 0, 0, 0, 17, 7, 7, 8.73, 0, 17, # HK1: sm0, tc0, tcno, mhno, TCHK, smhk, mhpass, TBCHK, xeploai, sotc_qua
219
- 0, 0, 0, 0, 17, 7, 7, 8.19, 0, 17, # HK2
220
- 0, 0, 0, 0, 17, 7, 7, 7.90, 0, 17, # HK3
221
- 0, 0, 0, 0, 17, 7, 7, 8.19, 0, 17, # HK4
222
- 0, 0, 0, 0, 19, 7, 6, 8.18, 0, 19, # HK5
223
- 0, 0, 5, 2, 19, 7, 5, 7.10, 1, 14 # HK6 (tc_qua = TCHK - tcno = 19 - 5 = 14)
224
- ]
225
- }
226
-
227
- sample_kinhte_example_full = {
228
- "name": "Trần Thị C",
229
- "student_id": "11418093",
230
- "major": "Kinh tế",
231
- "semester_data": [
232
- 0, 0, 0, 0, 16, 6, 6, 7.00, 1, 16, # HK1
233
- 0, 0, 0, 0, 18, 7, 7, 8.20, 1, 18, # HK2
234
- 0, 0, 0, 0, 17, 7, 7, 7.80, 1, 17, # HK3
235
- 0, 0, 0, 0, 17, 7, 6, 7.90, 1, 17, # HK4
236
- 0, 0, 0, 0, 19, 8, 5, 8.10, 0, 19, # HK5
237
- 0, 0, 8, 2, 19, 7, 6, 7.30, 1, 11 # HK6 (tc_qua = TCHK - tcno = 19 - 8 = 11)
238
- ]
239
  }
240
 
241
 
242
- # ===== Ví dụ cho mô hình "Dùng đặc trưng quan trọng" (matching image layout) =====
243
- # Features: Số tín chỉ đạt, Số tín chỉ không đạt, Điểm trung bình hệ 10
244
  sample_cntt_example_simple = {
245
- "name": "Nguyễn Văn B",
246
- "student_id": "10117368",
247
- "major": "Công nghệ thông tin",
248
- "semester_data": [
249
- 15, 0, 7.50, # Học kỳ 1: Số tín chỉ đạt, Số tín chỉ không đạt, Điểm trung bình hệ 10
250
- 17, 0, 8.10, # Học kỳ 2
251
- 18, 0, 7.90, # Học kỳ 3
252
- 17, 0, 7.80, # Học kỳ 4
253
- 19, 0, 8.30, # Học kỳ 5
254
- 19, 0, 7.20 # Học kỳ 6
255
- ]
256
  }
257
 
258
- sample_kinhte_example_simple = {
259
- "name": "Trần Thị B",
260
- "student_id": "11418092",
261
- "major": "Kinh tế",
262
- "semester_data": [
263
- 17, 0, 7.64, # Học kỳ 1
264
- 17, 0, 7.19, # Học kỳ 2
265
- 17, 0, 6.94, # Học kỳ 3
266
- 17, 0, 7.19, # Học kỳ 4
267
- 19, 0, 7.65, # Học kỳ 5
268
- 19, 0, 7.49 # Học kỳ 6
269
- ]
 
270
  }
271
 
272
 
273
  # ===== Thông tin cá nhân =====
274
- # Determine which example data to use based on sample_option and model_type
275
- current_sample_data = None
276
- if sample_option == "Dùng ví dụ mẫu ngành Công nghệ thông tin":
277
- current_sample_data = sample_cntt_example_full if model_type == "Dùng toàn bộ đặc trưng" else sample_cntt_example_simple
278
- elif sample_option == "Dùng ví dụ mẫu ngành Kinh tế":
279
- current_sample_data = sample_kinhte_example_full if model_type == "Dùng toàn bộ đặc trưng" else sample_kinhte_example_simple
280
-
281
- name = st.text_input("👤 Họ và tên", value=current_sample_data["name"] if current_sample_data else "")
282
- student_id = st.text_input("🎓 Mã sinh viên", value=current_sample_data["student_id"] if current_sample_data else "")
283
- # Find the correct index for the default major
284
- major_options = ["Công nghệ thông tin", "Kinh tế"]
285
- default_major_index = major_options.index(current_sample_data["major"]) if current_sample_data and current_sample_data["major"] in major_options else 0
286
- major = st.selectbox("📚 Ngành học", major_options, index=default_major_index)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
287
 
288
 
289
  st.write("---")
290
 
291
- # ===== Nhập thông tin học kỳ functions =====
292
  def input_semester_full(semester_label, default_values=None):
293
- # Ensure default_values has enough elements, pad with None if necessary
294
- padded_defaults = (default_values + [None] * 10)[:10] if default_values else [None] * 10
295
-
296
- with st.expander(f"📖 {semester_label}", expanded=True):
297
- col1, col2 = st.columns(2)
298
- with col1:
299
- somon0thi = st.number_input("Số môn không thi", min_value=0, value=int(padded_defaults[0]) if padded_defaults[0] is not None else 0, key=f"sm0_{semester_label}")
300
- sotc0thi = st.number_input("Số tín chỉ không thi", min_value=0, value=int(padded_defaults[1]) if padded_defaults[1] is not None else 0, key=f"tc0_{semester_label}")
301
- sotcno = st.number_input("Số tín chỉ nợ", min_value=0, value=int(padded_defaults[2]) if padded_defaults[2] is not None else 0, key=f"tcno_{semester_label}")
302
- mhno = st.number_input("Số môn không đạt", min_value=0, value=int(padded_defaults[3]) if padded_defaults[3] is not None else 0, key=f"mhno_{semester_label}")
303
- # Calculate default_tc_qua based on TCHK and sotcno if TCHK is available
304
- default_tc_qua_calc = (int(padded_defaults[4]) - int(padded_defaults[2])) if padded_defaults[4] is not None and padded_defaults[2] is not None else 0
305
- # Use the provided default_tc_qua if available, otherwise use the calculated one
306
- sotc_qua = st.number_input("Số tín chỉ qua môn", min_value=0, value=int(padded_defaults[9]) if padded_defaults[9] is not None else default_tc_qua_calc, key=f"tcqua_{semester_label}")
307
-
308
- with col2:
309
- TCHK = st.number_input("Tổng tín chỉ học kỳ", min_value=0, value=int(padded_defaults[4]) if padded_defaults[4] is not None else 0, key=f"tchk_{semester_label}")
310
- smhk = st.number_input("Số môn học kỳ", min_value=0, value=int(padded_defaults[5]) if padded_defaults[5] is not None else 0, key=f"smhk_{semester_label}")
311
- mhpass = st.number_input("Số môn đạt", min_value=0, value=int(padded_defaults[6]) if padded_defaults[6] is not None else 0, key=f"mhpass_{semester_label}")
312
- TBCHK = st.number_input("GPA", min_value=0.0, max_value=10.0, value=float(padded_defaults[7]) if padded_defaults[7] is not None else 0.0, step=0.01, key=f"gpa_{semester_label}")
313
- xep_loai_selected = st.selectbox("Xếp loại", list(range(7)), index=int(padded_defaults[8]) if padded_defaults[8] is not None and int(padded_defaults[8]) < 7 else 0, key=f"xeploai_{semester_label}")
314
-
315
- # Return the 10 values in the expected order for the full model
316
- return [somon0thi, sotc0thi, sotcno, mhno, TCHK, smhk, mhpass, TBCHK, xep_loai_selected, sotc_qua]
317
-
318
-
319
- def input_important_features(semester_label, default_values=None):
320
- # Ensure default_values has enough elements, pad with None if necessary (expecting 3 values)
321
- padded_defaults = (default_values + [None] * 3)[:3] if default_values else [None] * 3
322
-
323
- with st.expander(f"📘 {semester_label}", expanded=True):
324
- # Removed columns to match the image layout (vertical inputs)
325
- sotc_qua = st.number_input("Số tín chỉ đạt", min_value=0, value=int(padded_defaults[0]) if padded_defaults[0] is not None else 0, key=f"tcqua_imp_{semester_label}")
326
- sotc0thi = st.number_input("Số tín chỉ không đạt", min_value=0, value=int(padded_defaults[1]) if padded_defaults[1] is not None else 0, key=f"tc0thi_imp_{semester_label}")
327
- TBCHK = st.number_input("Điểm trung bình hệ 10", min_value=0.0, max_value=10.0, value=float(padded_defaults[2]) if padded_defaults[2] is not None else 0.0, step=0.01, key=f"gpa_imp_{semester_label}")
328
-
329
- # Return the 3 values in the expected order for the simple model
330
- return [sotc_qua, sotc0thi, TBCHK]
331
 
332
  # ===== Giao diện theo mô hình =====
333
  data = []
334
- semesters = ["HỌC KỲ I", "HỌC KỲ II", "HỌC KỲ III", "HỌC KỲ IV", "HỌC KỲ V", "HỌC KỲ VI"] # Assuming 6 semesters are still needed
335
 
336
  # Mô hình 1: "Dùng toàn bộ đặc trưng"
337
  if model_type == "Dùng toàn bộ đặc trưng":
338
  st.subheader("🔢 Nhập thông tin học kỳ chi tiết")
339
  for idx, sem in enumerate(semesters):
340
- example_data_for_sem = current_sample_data["semester_data"][idx*10:(idx+1)*10] if current_sample_data else None # Slicing 10 values
341
- data += input_semester_full(sem, example_data_for_sem)
 
342
  nganh = 0 if major == "Công nghệ thông tin" else 1
343
  final_features = np.array(data + [nganh]).reshape(1, -1)
344
 
345
- # Mô hình 2: "Dùng đặc trưng quan trọng"
346
  else:
347
- st.subheader("✨ Nhập thông tin rút gọn")
348
- for idx, sem in enumerate(semesters):
349
- example_data_for_sem = current_sample_data["semester_data"][idx*3:(idx+1)*3] if current_sample_data else None # Slicing 3 values
350
- data += input_important_features(sem, example_data_for_sem)
351
- # For the important features model, the 'major' feature is not included in the input vector
352
- final_features = np.array(data).reshape(1, -1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
353
 
354
 
355
  # ===== Predict =====
356
  if st.button("🎯 DỰ BÁO"):
357
- if model_type == "Dùng toàn bộ đặc trưng":
358
- # Ensure the number of features matches the model's expectation (6 semesters * 10 features/sem + 1 major = 61)
359
- if final_features.shape[1] != 61:
360
- st.error(f"Mismatch in number of features for 'Dùng toàn bộ đặc trưng' model. Expected 61, got {final_features.shape[1]}. Please check the input fields.")
361
- else:
362
- prediction = model_full.predict(final_features)
363
- sheet_name = "Trang tính1" # For full data model, save to Trang tính1
364
-
365
- if prediction[0] == 1:
366
- st.success(f"🎉 Chúc mừng bạn {name} - {major}! Bạn có khả năng tốt nghiệp đúng hạn!")
367
- st.balloons()
368
- else:
369
- st.error(f"⚠️ Bạn {name} - {major} cần cố gắng hơn! Có nguy cơ trễ hạn.")
370
- st.snow()
371
-
372
- # Ghi log lên Google Sheets
373
- save_to_gsheet(name, student_id, major, prediction, data,sheet_name) # Pass data to save function
374
-
375
- else: # model_type == "Dùng đặc trưng quan trọng"
376
- # Ensure the number of features matches the model's expectation (6 semesters * 3 features/sem = 18)
377
- if final_features.shape[1] != 18:
378
- st.error(f"Mismatch in number of features for 'Dùng đặc trưng quan trọng' model. Expected 18, got {final_features.shape[1]}. Please check the input fields.")
379
- else:
380
- prediction = model_important.predict(final_features)
381
- sheet_name = "Trang tính2" # For important features model, save to Trang tính2
382
-
383
- if prediction[0] == 1:
384
- st.success(f"🎉 Chúc mừng bạn {name} - {major}! Bạn có khả năng tốt nghiệp đúng hạn!")
385
- st.balloons()
386
- else:
387
- st.error(f"⚠️ Bạn {name} - {major} cần cố gắng hơn! Có nguy cơ trễ hạn.")
388
- st.snow()
389
-
390
- # Ghi log lên Google Sheets
391
- save_to_gsheet(name, student_id, major, prediction, data,sheet_name) # Pass data to save function
 
7
  import json
8
 
9
  # ===== Load models =====
10
+ model_full = joblib.load("random_forest_model_full.pkl")
11
+ model_important = joblib.load("random_forest_model_importainfeature.pkl")
 
 
 
 
 
 
12
 
13
  # ===== Setup page =====
14
  st.set_page_config(page_title="Dự đoán tốt nghiệp đúng hạn", page_icon="🎓", layout="wide")
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 =====
38
  @st.cache_resource
39
  def get_gsheet_client():
40
+     scope = ["https://spreadsheets.google.com/feeds", "https://www.googleapis.com/auth/drive"]
41
+     creds = ServiceAccountCredentials.from_json_keyfile_name("credentials.json", scope)
42
+     client = gspread.authorize(creds)
43
+     return 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
+     # 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 clarity in the sheet
75
+     base_headers = ["Họ tên", "MSV", "Khoa", "Dự báo", "Thời gian"]
76
+     if sheet_name == "Trang tính1": # Full model headers
77
+         semester_headers = []
78
+         for i in range(6):
79
+             semester_headers += [
80
+                 f"Số môn không thi - HK{i+1}",
81
+                 f"Số tín chỉ không thi - HK{i+1}",
82
+                 f"Số tín chỉ nợ - HK{i+1}",
83
+                 f"Số môn không đạt - HK{i+1}",
84
+                 f"Tổng tín chỉ học kỳ - HK{i+1}",
85
+                 f"Số môn học kỳ - HK{i+1}",
86
+                 f"Số môn đạt - HK{i+1}",
87
+                 f"GPA - HK{i+1}",
88
+                 f"Xếp loại - HK{i+1}"
89
+             ]
90
+         # Add nganh header
91
+         headers = base_headers + semester_headers + ["Ngành (0: CNTT, 1: Kinh tế)"]
92
+
93
+     elif sheet_name == "Trang tính2": # Important features model headers
94
+         semester_headers = []
95
+         for i in range(6):
96
+             semester_headers += [
97
+               �� f"Số tín chỉ đạt - HK{i+1}",
98
+                 f"Số tín chỉ nợ - HK{i+1}",
99
+                 f"Điểm trung bình - HK{i+1}"
100
+             ]
101
+         headers = base_headers + semester_headers
102
+
103
+
104
+     # If the sheet is empty or if headers are missing or mismatched, insert headers
105
+     if not all_values or len(all_values[0]) != len(headers):
106
+         sheet.clear() # Clear existing content if headers are wrong or missing
107
+         sheet.append_row(headers)  # Create headers
108
+
109
+     # Append data to the sheet
110
+     sheet.append_row(data_row)
111
+
112
+
113
+ # ===== List all sheet names =====
114
+ # def list_sheet_names(sheet_id):
115
+ #     client = get_gsheet_client()
116
+ #     sheet = client.open_by_key(sheet_id)
117
+ #    
118
+ #     # List all sheet names
119
+ #     sheet_names = [worksheet.title for worksheet in sheet.worksheets()]
120
+ #     return sheet_names
121
+
122
 
123
 
124
  # ===== HEADER =====
 
132
  # ===== Giao diện nhập ví dụ =====
133
  st.subheader("🔢 Chọn ví dụ mẫu hoặc nhập thông tin cá nhân")
134
 
135
+ sample_option = st.selectbox("📝 Chọn dụ:", ["Không 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ế"])
 
 
 
 
 
136
 
137
  # ===== Ví dụ mẫu cho mô hình "Dùng toàn bộ dữ liệu" =====
138
  sample_cntt_example_full = {
139
+     "name": "Nguyễn Văn A",
140
+     "student_id": "10117367",
141
+     "major": "Công nghệ thông tin",
142
+     "semester_data": [
143
+         0, 0, 0, 0, 17, 7, 7, 8.73, 0, # HK1
144
+         0, 0, 0, 0, 17, 7, 7, 8.19, 0, # HK2
145
+         0, 0, 0, 0, 17, 7, 7, 7.90, 0, # HK3
146
+         0, 0, 0, 0, 17, 7, 7, 8.19, 0, # HK4
147
+         0, 0, 0, 0, 19, 7, 6, 8.18, 0, # HK5
148
+         0, 0, 5, 2, 19, 7, 5, 7.10, 1 # HK6
149
+     ]
150
  }
151
 
152
+ # ===== dụ mẫu cho hình "Dùng hình đơn giản" =====
153
+ # ===== dụ cho hình "Dùng mô hình đơn giản" cho Kinh tế =====
154
+ sample_kinhte_example_simple = {
155
+     "name": "Trần Thị B",
156
+     "student_id": "11418092",
157
+     "major": "Kinh tế",
158
+     "semester_data": [
159
+         17, 7, 7.64,  # Học kỳ 1: Số tín chỉ đạt, Số tín chỉ nợ, GPA
160
+         17, 7, 7.19,  # Học kỳ 2: Số tín chỉ đạt, Số tín chỉ nợ, GPA
161
+         17, 7, 6.94,  # Học kỳ 3: Số tín chỉ đạt, Số tín chỉ nợ, GPA
162
+         17, 7, 7.19,  # Học kỳ 4: Số tín chỉ đạt, Số tín chỉ nợ, GPA
163
+         19, 7, 7.65,  # Học kỳ 5: Số tín chỉ đạt, Số tín chỉ nợ, GPA
164
+         19, 7, 7.49   # Học kỳ 6: Số tín chỉ đạt, Số tín chỉ nợ, GPA
165
+     ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  }
167
 
168
 
169
+ # ===== Ví dụ cho mô hình "Dùng hình đơn giản" cho CNTT =====
 
170
  sample_cntt_example_simple = {
171
+     "name": "Nguyễn Văn B",
172
+     "student_id": "10117368",
173
+     "major": "Công nghệ thông tin",
174
+     "semester_data": [
175
+         15, 8, 7.50,  # Học kỳ 1: Số tín chỉ đạt, Số tín chỉ nợ, GPA
176
+         17, 7, 8.10,  # Học kỳ 2: Số tín chỉ đạt, Số tín chỉ nợ, GPA
177
+         18, 7, 7.90,  # Học kỳ 3: Số tín chỉ đạt, Số tín chỉ nợ, GPA
178
+         17, 7, 7.80,  # Học kỳ 4: Số tín chỉ đạt, Số tín chỉ nợ, GPA
179
+         19, 8, 8.30,  # Học kỳ 5: Số tín chỉ đạt, Số tín chỉ nợ, GPA
180
+         19, 7, 7.20   # Học kỳ 6: Số tín chỉ đạt, Số tín chỉ nợ, GPA
181
+     ]
182
  }
183
 
184
+ # ===== Ví dụ cho mô hình "Dùng mô hình đơn giản" cho Kinh tế =====
185
+ sample_kinhte_example_full = {
186
+     "name": "Trần Thị C",
187
+     "student_id": "11418093",
188
+     "major": "Kinh tế",
189
+     "semester_data": [
190
+         0, 0, 0, 0, 16, 6, 6, 7.00, 1, # HK1
191
+         0, 0, 0, 0, 18, 7, 7, 8.20, 1, # HK2
192
+         0, 0, 0, 0, 17, 7, 7, 7.80, 1, # HK3
193
+         0, 0, 0, 0, 17, 7, 6, 7.90, 1, # HK4
194
+         0, 0, 0, 0, 19, 8, 5, 8.10, 0, # HK5
195
+         0, 0, 8, 2, 19, 7, 6, 7.30, 1 # HK6
196
+     ]
197
  }
198
 
199
 
200
  # ===== Thông tin cá nhân =====
201
+ # Determine which example data to use based on model type and selected option
202
+ if model_type == "Dùng toàn bộ đặc trưng":
203
+ if sample_option == "Dùng ví dụ mẫu ngành Công nghệ thông tin":
204
+ selected_example_data = sample_cntt_example_full
205
+ default_major_index = 0
206
+ elif sample_option == "Dùng dụ mẫu ngành Kinh tế":
207
+ selected_example_data = sample_kinhte_example_full
208
+ default_major_index = 1
209
+ else:
210
+ selected_example_data = None
211
+ default_major_index = 0 # Default to CNTT
212
+ else: # Dùng đặc trưng quan trọng
213
+ if sample_option == "Dùng dụ mẫu ngành Công nghệ thông tin":
214
+ selected_example_data = sample_cntt_example_simple
215
+ default_major_index = 0
216
+ elif sample_option == "Dùng ví dụ mẫu ngành Kinh tế":
217
+ selected_example_data = sample_kinhte_example_simple
218
+ default_major_index = 1
219
+ else:
220
+ selected_example_data = None
221
+ default_major_index = 0 # Default to CNTT
222
+
223
+
224
+ name_value = selected_example_data["name"] if selected_example_data else ""
225
+ student_id_value = selected_example_data["student_id"] if selected_example_data else ""
226
+
227
+
228
+ name = st.text_input("👤 Họ và tên", value=name_value)
229
+ student_id = st.text_input("🎓 Mã sinh viên", value=student_id_value)
230
+ major = st.selectbox("📚 Ngành học", ["Công nghệ thông tin", "Kinh tế"], index=default_major_index)
231
 
232
 
233
  st.write("---")
234
 
235
+ # ===== Nhập thông tin học kỳ =====
236
  def input_semester_full(semester_label, default_values=None):
237
+     with st.expander(f"📖 {semester_label}", expanded=True):
238
+         col1, col2 = st.columns(2)
239
+         with col1:
240
+             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}")
241
+             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}")
242
+             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}")
243
+             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}")
244
+             # Calculate default_tc_qua if possible, otherwise default to 0
245
+             default_tc_qua = 0
246
+             if default_values and len(default_values) > 4 and len(default_values) > 2:
247
+                 default_tc_qua = default_values[4] - default_values[2]
248
+             sotc_qua = st.number_input("Số tín chỉ qua môn", 0, value=default_tc_qua, key=f"tcqua_{semester_label}")
249
+
250
+         with col2:
251
+             TCHK = st.number_input("Tổng tín chỉ học kỳ", 0, value=default_values[4] if default_values and len(default_values) > 4 else 0, key=f"tchk_{semester_label}")
252
+             smhk = st.number_input("Số môn học kỳ", 0, value=default_values[5] if default_values and len(default_values) > 5 else 0, key=f"smhk_{semester_label}")
253
+             mhpass = st.number_input("Số môn đạt", 0, value=default_values[6] if default_values and len(default_values) > 6 else 0, key=f"mhpass_{semester_label}")
254
+             TBCHK = st.number_input("GPA", 0.0, 10.0, value=default_values[7] if default_values and len(default_values) > 7 else 0.0, step=0.01, key=f"gpa_{semester_label}")
255
+             xep_loai_selected = st.selectbox("Xếp loại", list(range(7)), index=default_values[8] if default_values and len(default_values) > 8 else 0, key=f"xeploai_{semester_label}")
256
+
257
+     return [somon0thi, sotc0thi, sotcno, mhno, TCHK, smhk, mhpass, TBCHK, xep_loai_selected] # Removed sotc_qua as it's calculated
258
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
 
260
  # ===== Giao diện theo mô hình =====
261
  data = []
262
+ semesters = ["HỌC KỲ I", "HỌC KỲ II", "HỌC KỲ III", "HỌC KỲ IV", "HỌC KỲ V", "HỌC KỲ VI"]
263
 
264
  # Mô hình 1: "Dùng toàn bộ đặc trưng"
265
  if model_type == "Dùng toàn bộ đặc trưng":
266
  st.subheader("🔢 Nhập thông tin học kỳ chi tiết")
267
  for idx, sem in enumerate(semesters):
268
+ 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 dụ mẫu ngành Kinh tế" else None
269
+ default_values = example_data["semester_data"][idx*9:(idx+1)*9] if example_data else None
270
+ data += input_semester_full(sem, default_values)
271
  nganh = 0 if major == "Công nghệ thông tin" else 1
272
  final_features = np.array(data + [nganh]).reshape(1, -1)
273
 
274
+ # Mô hình 2: "Dùng đặc trưng quan trọng" (Simplified layout as per image)
275
  else:
276
+     st.subheader("✨ Nhập thông tin rút gọn")
277
+     
278
+     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 dụ mẫu ngành Kinh tế" else None
279
+     semester_data_example = example_data["semester_data"] if example_data else [0, 0, 0] * 6 # Default to zeros if no example
280
+
281
+     # Row 1: HK I, HK II, HK III
282
+     cols1 = st.columns(3)
283
+     for idx, sem in enumerate(semesters[:3]):
284
+         with cols1[idx]:
285
+             with st.expander(f"📘 {sem}", expanded=True):
286
+                 default_values = semester_data_example[idx*3:(idx+1)*3]
287
+                 sotc_qua = st.number_input("Số tín chỉ đạt", 0, value=default_values[0] if len(default_values) > 0 else 0, key=f"tcqua_imp_{sem}")
288
+                 sotcno = st.number_input("Số tín chỉ nợ", 0, value=default_values[1] if len(default_values) > 1 else 0, key=f"tcno_imp_{sem}")
289
+                 TBCHK = st.number_input("Điểm trung bình", 0.0, 10.0, value=default_values[2] if len(default_values) > 2 else 0.0, step=0.01, key=f"gpa_imp_{sem}")
290
+                 data.extend([sotc_qua, sotcno, TBCHK]) # Collect data
291
+
292
+     # Row 2: HK IV, HK V, HK VI (assuming VI is included based on semesters list)
293
+     cols2 = st.columns(3)
294
+     for idx, sem in enumerate(semesters[3:]):
295
+         with cols2[idx]:
296
+             with st.expander(f"📘 {sem}", expanded=True):
297
+                 # Adjust index for example data: idx here is 0, 1, 2 for HK IV, V, VI
298
+                 example_idx = idx + 3 # Adjust to get correct data from the 6 semesters
299
+                 default_values = semester_data_example[example_idx*3:(example_idx+1)*3]
300
+                 sotc_qua = st.number_input("Số tín chỉ đạt", 0, value=default_values[0] if len(default_values) > 0 else 0, key=f"tcqua_imp_{sem}")
301
+                 sotcno = st.number_input("Số tín chỉ nợ", 0, value=default_values[1] if len(default_values) > 1 else 0, key=f"tcno_imp_{sem}")
302
+                 TBCHK = st.number_input("Điểm trung bình", 0.0, 10.0, value=default_values[2] if len(default_values) > 2 else 0.0, step=0.01, key=f"gpa_imp_{sem}")
303
+                 data.extend([sotc_qua, sotcno, TBCHK]) # Collect data
304
+
305
+     final_features = np.array(data).reshape(1, -1)
306
 
307
 
308
  # ===== Predict =====
309
  if st.button("🎯 DỰ BÁO"):
310
+     if model_type == "Dùng toàn bộ đặc trưng":
311
+         prediction = model_full.predict(final_features)
312
+         sheet_name = "Trang tính1"  # For full data model, save to Trang tính1
313
+
314
+     else:
315
+         prediction = model_important.predict(final_features)
316
+         sheet_name = "Trang tính2"  # For full data model, save to Trang tính1
317
+
318
+
319
+     if prediction[0] == 1:
320
+         st.success(f"🎉 Chúc mừng bạn {name} - {major}! Bạn có khả năng tốt nghiệp đúng hạn!")
321
+         st.balloons()
322
+     else:
323
+         st.error(f"⚠️ Bạn {name} - {major} cần cố gắng hơn! Có nguy cơ trễ hạn.")
324
+         st.snow()
325
+
326
+     # # Debugging: List all sheet names
327
+     # sheet_id = "1i7bDNvLVLXN93_e-FN0JLzpg1jb64Z_aEuyPjIfwbdQ"  # Use your actual Spreadsheet ID
328
+     # sheet_names = list_sheet_names(sheet_id)
329
+     # st.write("Available sheet names:", sheet_names)
330
+
331
+     # Ghi log lên Google Sheets
332
+     save_to_gsheet(name, student_id, major, prediction, data,sheet_name)  # Pass data to save function