SanthiSastra commited on
Commit
dc82a53
·
verified ·
1 Parent(s): 77aa332

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +71 -62
app.py CHANGED
@@ -1,7 +1,13 @@
1
  # app.py (Fast-build Hugging Face Gradio)
2
  # School Mark Analysis: RegNo, Name, Tamil, English, Maths, Science, Social
3
- # Features: Total, Average, Rank, Remark, subject averages, fail-count (1..5), top-3 overall, top-3 per subject,
4
- # search by RegNo, download result CSV
 
 
 
 
 
 
5
 
6
  import os
7
  import tempfile
@@ -9,43 +15,16 @@ import numpy as np
9
  import pandas as pd
10
  import gradio as gr
11
  import matplotlib.pyplot as plt
12
- import os
13
- import gradio as gr
14
-
15
- def logo_html():
16
- # Put the logo in the SAME folder as app.py
17
- candidates = [
18
- "logo.png", "logo.jpg", "logo.jpeg",
19
- "Logo.png", "Logo.jpg", "Logo.jpeg"
20
- ]
21
- for fn in candidates:
22
- if os.path.exists(fn):
23
- return f"""
24
- <div style="text-align:center; margin:6px 0 10px 0;">
25
- <img src="file={fn}" style="width:130px; display:block; margin:0 auto;" />
26
- <div style="font-size:30px; font-weight:800; color:#1E5AA8;">Amrita Manthana</div>
27
- <div style="font-size:18px; font-weight:800; color:#1E5AA8;">Prof.B.Santhi,SRC,SASTRA</div>
28
- </div>
29
- """
30
- # fallback (no image found)
31
- return """
32
- <div style="text-align:center; margin:6px 0 10px 0;">
33
- <div style="font-size:30px; font-weight:800; color:#1E5AA8;">Amrita Manthana</div>
34
- <div style="font-size:18px; font-weight:800; color:#1E5AA8;">Prof.B.Santhi,SRC,SASTRA</div>
35
- <div style="font-size:12px; color:#777;">(logo file not found in repo root)</div>
36
- </div>
37
- """
38
-
39
- # Use this inside your Blocks:
40
- # with gr.Blocks(...) as demo:
41
- # gr.HTML(logo_html())
42
-
43
 
44
  SUBJECTS_DEFAULT = ["Tamil", "English", "Maths", "Science", "Social"]
45
  ID_COL_DEFAULT = "RegNo"
46
  NAME_COL_DEFAULT = "Name"
47
 
48
 
 
 
 
49
  def _clean_columns(df: pd.DataFrame) -> pd.DataFrame:
50
  df = df.copy()
51
  df.columns = [c.strip() for c in df.columns]
@@ -73,7 +52,6 @@ def _validate_and_prepare(df: pd.DataFrame, id_col: str, name_col: str, subjects
73
  def _remark(avg: float, failed_subjects: int) -> str:
74
  if failed_subjects > 0:
75
  return "Fail"
76
- # Only pass students reach here
77
  if avg >= 80:
78
  return "Distinction"
79
  if 60 <= avg <= 79:
@@ -83,8 +61,16 @@ def _remark(avg: float, failed_subjects: int) -> str:
83
  return "Pass"
84
 
85
 
86
- def compute_marks(df: pd.DataFrame, pass_mark: int = 35, id_col: str = ID_COL_DEFAULT,
87
- name_col: str = NAME_COL_DEFAULT, subjects: list[str] = SUBJECTS_DEFAULT):
 
 
 
 
 
 
 
 
88
  df = _validate_and_prepare(df, id_col, name_col, subjects)
89
 
90
  out = df.copy()
@@ -140,6 +126,9 @@ def compute_marks(df: pd.DataFrame, pass_mark: int = 35, id_col: str = ID_COL_DE
140
  return out, subj_avg, fail_dist, top3_overall, top3_each_subject, summary
141
 
142
 
 
 
 
143
  def plot_subject_avg(subj_avg: pd.DataFrame):
144
  fig, ax = plt.subplots(figsize=(7, 4))
145
  ax.bar(subj_avg["Subject"], subj_avg["Class_Average"])
@@ -149,6 +138,7 @@ def plot_subject_avg(subj_avg: pd.DataFrame):
149
  ax.set_ylim(0, 100)
150
  plt.xticks(rotation=25, ha="right")
151
  plt.tight_layout()
 
152
  return fig
153
 
154
 
@@ -165,9 +155,13 @@ def plot_remark_distribution(result_df: pd.DataFrame):
165
  ax.set_ylabel("Number of Students")
166
  plt.xticks(rotation=20, ha="right")
167
  plt.tight_layout()
 
168
  return fig
169
 
170
 
 
 
 
171
  def load_csv(file_obj):
172
  if file_obj is None:
173
  return None, "Please upload a CSV.", None
@@ -207,9 +201,7 @@ def search_regno(result_df, regno_value):
207
  if not regno_value:
208
  return "Enter RegNo to search.", pd.DataFrame()
209
 
210
- # Exact match (string or numeric)
211
  col = result_df[ID_COL_DEFAULT]
212
- res = None
213
  if pd.api.types.is_numeric_dtype(col):
214
  try:
215
  q = float(regno_value)
@@ -219,41 +211,48 @@ def search_regno(result_df, regno_value):
219
  else:
220
  res = result_df[col.astype(str) == str(regno_value)]
221
 
222
- if res is None or res.empty:
223
  return "No matching record found.", pd.DataFrame()
224
  return f"Found {len(res)} record(s).", res
225
 
226
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
  CSS = """
228
  #titleblock {text-align:center; margin-top: 6px; margin-bottom: 8px;}
229
  #t1 {font-size:30px; font-weight:800; color:#1E5AA8;}
230
  #t2 {font-size:18px; font-weight:800; color:#1E5AA8;}
231
  """
232
 
233
- def header_html():
234
- if os.path.exists("logo.jpg"):
235
- return """
236
- <div id="titleblock">
237
- <img src="file=logo.jpg" style="width:110px; display:block; margin:0 auto;" />
238
- <div id="t1">Amrita Manthana</div>
239
- <div id="t2">Prof.B.Santhi,SRC,SASTRA</div>
240
- </div>
241
- """
242
- return """
243
- <div id="titleblock">
244
  <div id="t1">Amrita Manthana</div>
245
  <div id="t2">Prof.B.Santhi,SRC,SASTRA</div>
246
  </div>
247
- """
248
-
249
-
250
-
251
-
252
- with gr.Blocks(css=CSS, title="School Mark Analysis") as demo:
253
- gr.HTML(header_html())
254
 
255
  df_state = gr.State(None)
256
- result_state = gr.State(None) # stores result_df for search
257
 
258
  with gr.Row():
259
  with gr.Column(scale=1, min_width=340):
@@ -273,12 +272,20 @@ with gr.Blocks(css=CSS, title="School Mark Analysis") as demo:
273
  with gr.Column(scale=2):
274
  with gr.Tabs():
275
  with gr.Tab("Result Table"):
276
- result_table = gr.Dataframe(label="Result (Total, Average, Rank, Remark, Failed_Subjects)", interactive=False, wrap=True)
 
 
 
 
277
  with gr.Tab("Subject Averages"):
278
  subj_avg_table = gr.Dataframe(label="Subject-wise Averages", interactive=False, wrap=True)
279
  avg_plot = gr.Plot(label="Bar Chart: Subject-wise Average")
280
  with gr.Tab("Fail Counts"):
281
- fail_dist_table = gr.Dataframe(label="Students failed in 1/2/3/4/5 subjects", interactive=False, wrap=True)
 
 
 
 
282
  with gr.Tab("Toppers"):
283
  top3_overall_table = gr.Dataframe(label="Overall Top 3", interactive=False, wrap=True)
284
  top3_each_subject_table = gr.Dataframe(label="Top 3 in each subject", interactive=False, wrap=True)
@@ -304,8 +311,10 @@ with gr.Blocks(css=CSS, title="School Mark Analysis") as demo:
304
  run_btn.click(
305
  run_and_store,
306
  inputs=[df_state, pass_mark],
307
- outputs=[summary, result_table, subj_avg_table, fail_dist_table, top3_overall_table, top3_each_subject_table,
308
- avg_plot, remark_plot, download_file, result_state]
 
 
309
  )
310
 
311
  search_btn.click(
 
1
  # app.py (Fast-build Hugging Face Gradio)
2
  # School Mark Analysis: RegNo, Name, Tamil, English, Maths, Science, Social
3
+ # Features:
4
+ # - Total, Average, Rank, Remark
5
+ # - Subject-wise average + bar chart
6
+ # - Fail-count distribution (failed in 1..5 subjects)
7
+ # - Top-3 overall, Top-3 in each subject
8
+ # - Search by RegNo
9
+ # - Download final result CSV
10
+ # - Logo display (reliable on Hugging Face) using gr.Image + Pillow
11
 
12
  import os
13
  import tempfile
 
15
  import pandas as pd
16
  import gradio as gr
17
  import matplotlib.pyplot as plt
18
+ from PIL import Image
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
  SUBJECTS_DEFAULT = ["Tamil", "English", "Maths", "Science", "Social"]
21
  ID_COL_DEFAULT = "RegNo"
22
  NAME_COL_DEFAULT = "Name"
23
 
24
 
25
+ # -----------------------------
26
+ # Utilities
27
+ # -----------------------------
28
  def _clean_columns(df: pd.DataFrame) -> pd.DataFrame:
29
  df = df.copy()
30
  df.columns = [c.strip() for c in df.columns]
 
52
  def _remark(avg: float, failed_subjects: int) -> str:
53
  if failed_subjects > 0:
54
  return "Fail"
 
55
  if avg >= 80:
56
  return "Distinction"
57
  if 60 <= avg <= 79:
 
61
  return "Pass"
62
 
63
 
64
+ # -----------------------------
65
+ # Core computations
66
+ # -----------------------------
67
+ def compute_marks(
68
+ df: pd.DataFrame,
69
+ pass_mark: int = 35,
70
+ id_col: str = ID_COL_DEFAULT,
71
+ name_col: str = NAME_COL_DEFAULT,
72
+ subjects: list[str] = SUBJECTS_DEFAULT,
73
+ ):
74
  df = _validate_and_prepare(df, id_col, name_col, subjects)
75
 
76
  out = df.copy()
 
126
  return out, subj_avg, fail_dist, top3_overall, top3_each_subject, summary
127
 
128
 
129
+ # -----------------------------
130
+ # Plots (close figs to avoid memory growth)
131
+ # -----------------------------
132
  def plot_subject_avg(subj_avg: pd.DataFrame):
133
  fig, ax = plt.subplots(figsize=(7, 4))
134
  ax.bar(subj_avg["Subject"], subj_avg["Class_Average"])
 
138
  ax.set_ylim(0, 100)
139
  plt.xticks(rotation=25, ha="right")
140
  plt.tight_layout()
141
+ plt.close(fig)
142
  return fig
143
 
144
 
 
155
  ax.set_ylabel("Number of Students")
156
  plt.xticks(rotation=20, ha="right")
157
  plt.tight_layout()
158
+ plt.close(fig)
159
  return fig
160
 
161
 
162
+ # -----------------------------
163
+ # Gradio handlers
164
+ # -----------------------------
165
  def load_csv(file_obj):
166
  if file_obj is None:
167
  return None, "Please upload a CSV.", None
 
201
  if not regno_value:
202
  return "Enter RegNo to search.", pd.DataFrame()
203
 
 
204
  col = result_df[ID_COL_DEFAULT]
 
205
  if pd.api.types.is_numeric_dtype(col):
206
  try:
207
  q = float(regno_value)
 
211
  else:
212
  res = result_df[col.astype(str) == str(regno_value)]
213
 
214
+ if res.empty:
215
  return "No matching record found.", pd.DataFrame()
216
  return f"Found {len(res)} record(s).", res
217
 
218
 
219
+ # -----------------------------
220
+ # Logo loader (reliable in HF)
221
+ # -----------------------------
222
+ def load_logo():
223
+ for fn in ["logo.jpg", "logo.png", "logo.jpeg", "Logo.jpg", "Logo.png", "Logo.jpeg"]:
224
+ if os.path.exists(fn):
225
+ try:
226
+ return Image.open(fn)
227
+ except Exception:
228
+ return None
229
+ return None
230
+
231
+
232
+ # -----------------------------
233
+ # UI
234
+ # -----------------------------
235
  CSS = """
236
  #titleblock {text-align:center; margin-top: 6px; margin-bottom: 8px;}
237
  #t1 {font-size:30px; font-weight:800; color:#1E5AA8;}
238
  #t2 {font-size:18px; font-weight:800; color:#1E5AA8;}
239
  """
240
 
241
+ with gr.Blocks(css=CSS, title="School Mark Analysis") as demo:
242
+ # Logo + title
243
+ logo = load_logo()
244
+ if logo is not None:
245
+ gr.Image(value=logo, show_label=False, interactive=False, height=160)
246
+
247
+ gr.HTML("""
248
+ <div id="titleblock" style="margin-top:-10px;">
 
 
 
249
  <div id="t1">Amrita Manthana</div>
250
  <div id="t2">Prof.B.Santhi,SRC,SASTRA</div>
251
  </div>
252
+ """)
 
 
 
 
 
 
253
 
254
  df_state = gr.State(None)
255
+ result_state = gr.State(None)
256
 
257
  with gr.Row():
258
  with gr.Column(scale=1, min_width=340):
 
272
  with gr.Column(scale=2):
273
  with gr.Tabs():
274
  with gr.Tab("Result Table"):
275
+ result_table = gr.Dataframe(
276
+ label="Result (Total, Average, Rank, Remark, Failed_Subjects)",
277
+ interactive=False,
278
+ wrap=True
279
+ )
280
  with gr.Tab("Subject Averages"):
281
  subj_avg_table = gr.Dataframe(label="Subject-wise Averages", interactive=False, wrap=True)
282
  avg_plot = gr.Plot(label="Bar Chart: Subject-wise Average")
283
  with gr.Tab("Fail Counts"):
284
+ fail_dist_table = gr.Dataframe(
285
+ label="Students failed in 1/2/3/4/5 subjects",
286
+ interactive=False,
287
+ wrap=True
288
+ )
289
  with gr.Tab("Toppers"):
290
  top3_overall_table = gr.Dataframe(label="Overall Top 3", interactive=False, wrap=True)
291
  top3_each_subject_table = gr.Dataframe(label="Top 3 in each subject", interactive=False, wrap=True)
 
311
  run_btn.click(
312
  run_and_store,
313
  inputs=[df_state, pass_mark],
314
+ outputs=[
315
+ summary, result_table, subj_avg_table, fail_dist_table, top3_overall_table, top3_each_subject_table,
316
+ avg_plot, remark_plot, download_file, result_state
317
+ ]
318
  )
319
 
320
  search_btn.click(