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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +240 -148
app.py CHANGED
@@ -7,8 +7,14 @@ from datetime import datetime
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")
@@ -17,7 +23,7 @@ st.set_page_config(page_title="Dự đoán tốt nghiệp đúng hạn", page_ic
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;
@@ -33,16 +39,43 @@ st.markdown("""
33
  }
34
 
35
  /* Make number_input, selectbox, and text_input shorter using data-testid */
36
- div[data-testid="stNumberInput"] {
37
- max-width: 200px; /* Adjust width as needed */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  }
39
- div[data-testid="stSelectbox"] {
40
- max-width: 300px; /* Adjust width as needed */
 
 
41
  }
42
- div[data-testid="stTextInput"] {
43
- max-width: 350px; /* Adjust width as needed */
 
 
 
 
44
  }
45
 
 
46
  /* Ensure inputs inside columns don't overflow */
47
  div[data-testid="stNumberInput"] input[type="number"],
48
  div[data-testid="stSelectbox"] select,
@@ -56,27 +89,34 @@ st.markdown("""
56
  # ===== GSheet integration =====
57
  @st.cache_resource
58
  def get_gsheet_client():
59
- scope = ["https://spreadsheets.google.com/feeds", "https://www.googleapis.com/auth/drive"]
60
- creds = ServiceAccountCredentials.from_json_keyfile_name("credentials.json", scope)
61
- client = gspread.authorize(creds)
62
- return client
 
 
 
 
 
 
 
63
 
64
  # ===== Save to Google Sheets =====
65
  def save_to_gsheet(name, student_id, major, prediction, semester_data, sheet_name="Trang tính1"):
66
  # Get the current date and time
67
  now = datetime.now()
68
  current_time = now.strftime("%Y-%m-%d %H:%M:%S")
69
-
70
  # Ensure prediction is in the expected format (0 or 1)
71
  if isinstance(prediction, np.ndarray):
72
  prediction = int(prediction[0]) # Convert to int if it's ndarray
73
-
74
  elif isinstance(prediction, np.int64):
75
  prediction = int(prediction) # Convert np.int64 to int
76
-
77
  # Flatten semester data to individual columns for each semester
78
  semester_data_flat = [str(val) for val in semester_data] # Convert all semester data values to strings
79
-
80
  # Create a data row to insert into the sheet
81
  data_row = [name, student_id, major, prediction, current_time] + semester_data_flat
82
 
@@ -85,40 +125,50 @@ def save_to_gsheet(name, student_id, major, prediction, semester_data, sheet_nam
85
 
86
  # Use the Spreadsheet ID (replace with your actual ID)
87
  sheet_id = "1i7bDNvLVLXN93_e-FN0JLzpg1jb64Z_aEuyPjIfwbdQ" # Use your actual ID here
88
- sheet = client.open_by_key(sheet_id).worksheet(sheet_name)
 
 
 
 
 
 
 
 
89
 
90
  # Get all values from the sheet to check if the first row is empty (i.e., headers not created yet)
91
  all_values = sheet.get_all_values()
92
 
93
- # If the sheet is empty or if headers are missing, insert headers
94
- if not all_values or len(all_values[0]) != (len(semester_data_flat) + 5): # Ensure number of columns matches
95
- headers = ["Họ và tên", "MSV", "Khoa", "Dự báo", "Thời gian"] + [
96
- f"Số môn không thi - HK{i+1}" for i in range(6)] + [
97
- f"Số tín chỉ không thi - HK{i+1}" for i in range(6)] + [
98
- f"Số tín chỉ nợ - HK{i+1}" for i in range(6)] + [
99
- f"Số môn không đạt - HK{i+1}" for i in range(6)] + [
100
- f"Số tín chỉ qua môn - HK{i+1}" for i in range(6)] + [
101
- f"Tổng tín chỉ học kỳ - HK{i+1}" for i in range(6)] + [
102
- f"Số môn học kỳ - HK{i+1}" for i in range(6)] + [
103
- f"Số môn đạt - HK{i+1}" for i in range(6)] + [
104
- f"GPA - HK{i+1}" for i in range(6)] + [
105
- f"Xếp loại - HK{i+1}" for i in range(6)]
106
-
107
- sheet.append_row(headers) # Create headers if missing
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,7 +182,12 @@ model_type = st.sidebar.selectbox("🧠 Chọn mô hình dự báo:", ["Dùng to
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 = {
@@ -140,160 +195,197 @@ sample_cntt_example_full = {
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,
144
- 0, 0, 0, 0, 17, 7, 7, 8.19, 0,
145
- 0, 0, 0, 0, 17, 7, 7, 7.90, 0,
146
- 0, 0, 0, 0, 17, 7, 7, 8.19, 0,
147
- 0, 0, 0, 0, 19, 7, 6, 8.18, 0,
148
- 0, 0, 5, 2, 19, 7, 5, 7.10, 1
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
- # ===== dụ cho hình "Dùng hình đơn giản" cho CNTT =====
171
  sample_cntt_example_simple = {
172
  "name": "Nguyễn Văn B",
173
  "student_id": "10117368",
174
  "major": "Công nghệ thông tin",
175
  "semester_data": [
176
- 15, 8, 7.50, # Học kỳ 1: Số tín chỉ đạt, Số tín chỉ nợ, GPA
177
- 17, 7, 8.10, # Học kỳ 2: Số tín chỉ đạt, Số tín chỉ nợ, GPA
178
- 18, 7, 7.90, # Học kỳ 3: Số tín chỉ đạt, Số tín chỉ nợ, GPA
179
- 17, 7, 7.80, # Học kỳ 4: Số tín chỉ đạt, Số tín chỉ nợ, GPA
180
- 19, 8, 8.30, # Học kỳ 5: Số tín chỉ đạt, Số tín chỉ nợ, GPA
181
- 19, 7, 7.20 # Học kỳ 6: Số tín chỉ đạt, Số tín chỉ nợ, GPA
182
  ]
183
  }
184
 
185
- # ===== Ví dụ cho mô hình "Dùng mô hình đơn giản" cho Kinh tế =====
186
- sample_kinhte_example_full = {
187
- "name": "Trần Thị C",
188
- "student_id": "11418093",
189
  "major": "Kinh tế",
190
  "semester_data": [
191
- 0, 0, 0, 0, 16, 6, 6, 7.00, 1,
192
- 0, 0, 0, 0, 18, 7, 7, 8.20, 1,
193
- 0, 0, 0, 0, 17, 7, 7, 7.80, 1,
194
- 0, 0, 0, 0, 17, 7, 6, 7.90, 1,
195
- 0, 0, 0, 0, 19, 8, 5, 8.10, 0,
196
- 0, 0, 8, 2, 19, 7, 6, 7.30, 1
197
  ]
198
  }
199
 
 
200
  # ===== Thông tin cá nhân =====
 
 
201
  if sample_option == "Dùng ví dụ mẫu ngành Công nghệ thông tin":
202
- name = st.text_input("👤 Họ tên", value=sample_cntt_example_full["name"])
203
- student_id = st.text_input("🎓 Mã sinh viên", value=sample_cntt_example_full["student_id"])
204
- major = st.selectbox("📚 Ngành học", ["Công nghệ thông tin", "Kinh tế"], index=0)
205
  elif sample_option == "Dùng ví dụ mẫu ngành Kinh tế":
206
- name = st.text_input("👤 Họ tên", value=sample_kinhte_example_simple["name"])
207
- student_id = st.text_input("🎓 Mã sinh viên", value=sample_kinhte_example_simple["student_id"])
208
- major = st.selectbox("📚 Ngành học", ["Công nghệ thông tin", "Kinh tế"], index=1)
209
- else:
210
- name = st.text_input("👤 Họ tên")
211
- student_id = st.text_input("🎓 sinh viên")
212
- major = st.selectbox("📚 Ngành học", ["Công nghệ thông tin", "Kinh tế"])
 
 
213
 
214
  st.write("---")
215
 
216
- # ===== Nhập thông tin học kỳ =====
217
- def input_semester(semester_label, default_values=None):
 
 
 
218
  with st.expander(f"📖 {semester_label}", expanded=True):
219
  col1, col2 = st.columns(2)
220
  with col1:
221
- 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}")
222
- 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}")
223
- sotcno = st.number_input("Số tín chỉ nợ", 0, value=default_values[2] if default_values else 0, key=f"tcno_{semester_label}")
224
- 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}")
225
- try:
226
- default_tc_qua = default_values[4] - default_values[2] if default_values and len(default_values) >= 5 else 0
227
- except:
228
- default_tc_qua = 0
229
- sotc_qua = st.number_input("Số tín chỉ qua môn", 0, value=default_tc_qua, key=f"tcqua_{semester_label}")
230
  with col2:
231
- 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}")
232
- 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}")
233
- mhpass = st.number_input("Số môn đạt", 0, value=default_values[6] if default_values else 0, key=f"mhpass_{semester_label}")
234
- 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}")
235
- 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}")
 
 
236
  return [somon0thi, sotc0thi, sotcno, mhno, TCHK, smhk, mhpass, TBCHK, xep_loai_selected, sotc_qua]
237
 
 
238
  def input_important_features(semester_label, default_values=None):
 
 
 
239
  with st.expander(f"📘 {semester_label}", expanded=True):
240
- col1, col2 = st.columns(2)
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_{semester_label}")
243
- sotcno = st.number_input("Số tín chỉ nợ", 0, value=default_values[1] if default_values else 0, key=f"tcno_imp_{semester_label}")
244
- with col2:
245
- TBCHK = st.number_input("Điểm trung bình", 0.0, 10.0, value=default_values[2] if default_values else 0.0, step=0.01, key=f"gpa_imp_{semester_label}")
246
- return [sotc_qua, sotcno, TBCHK]
247
 
248
  # ===== Giao diện theo mô hình =====
249
  data = []
250
- 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"]
251
 
252
- # Mô hình 1: "Dùng toàn bộ dữ liệu"
253
  if model_type == "Dùng toàn bộ đặc trưng":
254
  st.subheader("🔢 Nhập thông tin học kỳ chi tiết")
255
  for idx, sem in enumerate(semesters):
256
- 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
257
- default_values = example_data["semester_data"][idx*9:(idx+1)*9] if example_data else None
258
- data += input_semester(sem, default_values)
259
  nganh = 0 if major == "Công nghệ thông tin" else 1
260
  final_features = np.array(data + [nganh]).reshape(1, -1)
261
 
262
- # Mô hình 2: "Dùng hình đơn giản"
263
  else:
264
-
265
- # Ensure the example defaults to "Không ví dụ" when model type is 2
266
-
267
  st.subheader("✨ Nhập thông tin rút gọn")
268
- # Allow selecting an example
269
  for idx, sem in enumerate(semesters):
270
- 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
271
- default_values = example_data["semester_data"][idx*3:(idx+1)*3] if example_data else None # Fix this line to slice 3 values per semester (Số tín chỉ đạt, Số tín chỉ nợ, GPA)
272
- data += input_important_features(sem, default_values)
273
  final_features = np.array(data).reshape(1, -1)
274
 
 
275
  # ===== Predict =====
276
  if st.button("🎯 DỰ BÁO"):
277
  if model_type == "Dùng toàn bộ đặc trưng":
278
- prediction = model_full.predict(final_features)
279
- sheet_name = "Trang tính1" # For full data model, save to Trang tính1
280
-
281
- else:
282
- prediction = model_important.predict(final_features)
283
- sheet_name = "Trang tính2" # For full data model, save to Trang tính1
284
-
285
-
286
- if prediction[0] == 1:
287
- st.success(f"🎉 Chúc mừng bạn {name} - {major}! Bạn có khả năng tốt nghiệp đúng hạn!")
288
- st.balloons()
289
- else:
290
- st.error(f"⚠️ Bạn {name} - {major} cần cố gắng hơn! Có nguy cơ trễ hạn.")
291
- st.snow()
292
-
293
- # # Debugging: List all sheet names
294
- # sheet_id = "1i7bDNvLVLXN93_e-FN0JLzpg1jb64Z_aEuyPjIfwbdQ" # Use your actual Spreadsheet ID
295
- # sheet_names = list_sheet_names(sheet_id)
296
- # st.write("Available sheet names:", sheet_names)
297
-
298
- # Ghi log lên Google Sheets
299
- save_to_gsheet(name, student_id, major, prediction, data,sheet_name) # Pass data to save function
 
 
 
 
 
 
 
 
 
 
 
 
 
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")
 
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;
 
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,
 
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
 
 
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ọ và 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ọ 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
  # ===== 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 = {
 
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