Turbiling commited on
Commit
d75abc7
·
verified ·
1 Parent(s): e3e29b3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +83 -128
app.py CHANGED
@@ -1,7 +1,6 @@
1
- # app.py
2
  """
3
  Educational Pre-Audit (EPA) Generator
4
- Generates customized school audit reports (English Word .docx or Urdu Markdown .md)
5
  Developed by Najaf Ali Sharqi
6
  """
7
 
@@ -19,46 +18,41 @@ from docx.enum.text import WD_PARAGRAPH_ALIGNMENT, WD_LINE_SPACING
19
  from docx.oxml import OxmlElement
20
  from docx.oxml.ns import qn
21
 
22
- # Gemini configuration (optional)
23
- GENAI_AVAILABLE = False
24
- try:
25
- import google.generativeai as genai
26
- key = os.getenv("GOOGLE_API_KEY")
27
- if key:
28
- genai.configure(api_key=key)
29
- GENAI_AVAILABLE = True
30
- except Exception:
31
- GENAI_AVAILABLE = False
32
 
33
 
34
  def sanitize_text(text: str) -> str:
35
- """Remove symbols and emojis"""
36
  return "".join(ch for ch in text if ord(ch) >= 32)
37
 
38
 
39
- def build_prompt(school_data: dict, language: str) -> str:
40
- prompt = (
41
  "You are EduAudit AI, a professional educational auditor.\n"
42
- "Generate a detailed Educational Pre-Audit Report with clearly separated sections:\n"
43
  "1. Executive Summary\n2. Domain-wise Findings\n3. Key Findings Table\n"
44
  "4. Recommendations and SMART Action Plan\n5. Overall Audit Rating (1–5)\n6. Conclusion\n\n"
45
- "Each heading must be bold and start on a new line. Avoid any emojis or special characters.\n"
46
- "Use clear, professional tone.\n\n"
47
- f"DATA:\n{json.dumps(school_data, ensure_ascii=False, indent=2)}\n\n"
48
- f"Language: {language}\n"
49
  )
50
- return prompt
51
 
52
 
53
- def generate_with_gemini(prompt: str):
54
- if not GENAI_AVAILABLE:
55
- raise RuntimeError("Gemini API not configured.")
56
- model = genai.GenerativeModel("gemini-2.5-flash")
57
- r = model.generate_content(prompt)
58
- return r.text
 
 
59
 
60
 
61
  def set_paragraph_format(paragraph, font_size=12, spacing=1.5, bold=False):
 
62
  for run in paragraph.runs:
63
  run.font.name = "Times New Roman"
64
  run.font.size = Pt(font_size)
@@ -67,8 +61,10 @@ def set_paragraph_format(paragraph, font_size=12, spacing=1.5, bold=False):
67
 
68
 
69
  def create_docx(report: str, data: dict, charts: list):
 
70
  doc = Document()
71
 
 
72
  section = doc.sections[0]
73
  header = section.header.paragraphs[0]
74
  header.text = "Developed by Najaf Ali Sharqi"
@@ -80,12 +76,15 @@ def create_docx(report: str, data: dict, charts: list):
80
  set_paragraph_format(title, bold=True)
81
 
82
  # Metadata
83
- p1 = doc.add_paragraph(f"Generated on: {datetime.now():%Y-%m-%d %H:%M}")
84
- p2 = doc.add_paragraph(f"Institution: {data.get('name','')}")
85
- p3 = doc.add_paragraph(f"Principal: {data.get('principal','')}")
86
- p4 = doc.add_paragraph(f"Location: {data.get('location','')}")
87
- p5 = doc.add_paragraph(f"Level: {data.get('level','')}")
88
- for p in [p1, p2, p3, p4, p5]:
 
 
 
89
  set_paragraph_format(p)
90
 
91
  doc.add_paragraph()
@@ -97,26 +96,33 @@ def create_docx(report: str, data: dict, charts: list):
97
  cap.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
98
  set_paragraph_format(cap)
99
 
100
- # Split report by headings
101
  lines = report.split("\n")
102
  for line in lines:
103
- if not line.strip():
 
104
  continue
105
- if line.strip().endswith(":") or line.strip().endswith("۔") or line.strip().endswith("."):
106
- para = doc.add_paragraph(line.strip())
107
- para.style = "Normal"
 
 
 
 
 
108
  set_paragraph_format(para, bold=True)
 
109
  else:
110
- para = doc.add_paragraph(line.strip())
111
- para.style = "Normal"
112
- set_paragraph_format(para)
113
 
114
- # Footer with page number
115
  footer = section.footer.paragraphs[0]
116
- fld = OxmlElement('w:fldSimple')
117
- fld.set(qn('w:instr'), 'PAGE')
118
  footer._p.append(fld)
119
 
 
120
  bio = io.BytesIO()
121
  doc.save(bio)
122
  bio.seek(0)
@@ -128,7 +134,7 @@ def create_charts(data):
128
  obs = float(data.get("observation_average", 0))
129
  fb = float(data.get("feedback_average", 0))
130
  fig, ax = plt.subplots(figsize=(5, 3))
131
- ax.bar(["Observation", "Feedback"], [obs, fb], color=["#5DADE2", "#48C9B0"])
132
  ax.set_ylim(0, 5)
133
  ax.set_ylabel("Score (1-5)")
134
  plt.tight_layout()
@@ -136,81 +142,41 @@ def create_charts(data):
136
  fig.savefig(buf, format="png")
137
  plt.close(fig)
138
  charts.append((buf.getvalue(), "Observation vs Feedback (1–5 scale)"))
139
-
140
- teachers = int(data.get("total_teachers", 0))
141
- nonteach = int(data.get("total_non_teaching_staff", 0))
142
- fig2, ax2 = plt.subplots()
143
- ax2.pie([teachers, nonteach], labels=["Teachers", "Non-Teaching"], autopct="%1.0f%%")
144
- plt.tight_layout()
145
- buf2 = io.BytesIO()
146
- fig2.savefig(buf2, format="png")
147
- plt.close(fig2)
148
- charts.append((buf2.getvalue(), "Staff Composition"))
149
  return charts
150
 
151
 
152
- def build_urdu_md(report):
153
- lines = report.split("\n")
154
- text = "<div dir='rtl' style='line-height:1.5;'>\n\n"
155
- for line in lines:
156
- if not line.strip():
157
- continue
158
- if line.strip().endswith("۔") or line.strip().endswith(":"):
159
- text += f"<b>{line.strip()}</b>\n\n"
160
- else:
161
- text += line.strip() + "\n\n"
162
- text += "</div>"
163
- return text.encode("utf-8")
164
 
165
 
166
- def sample_report(data, language):
167
- name = data.get("name", "School")
168
- if language == "Urdu":
169
- return (
170
- f"خلاصہ:\n{name} ایک تعلیمی ادارہ ہے جو مسلسل بہتری کی سمت میں گامزن ہے۔\n\n"
171
- "مشاہداتی اوسط: 4.2\nفیڈبیک اوسط: 4.0\n\n"
172
- "تجاویز:\n1. اساتذہ کی تربیت میں اضافہ کیا جائے۔\n2. انفراسٹرکچر میں بہتری لائی جائے۔\n\n"
173
- "نتیجہ:\nادارہ مجموعی طور پر اچھی کارکردگی دکھا رہا ہے۔\n\n"
174
- "Developed by Najaf Ali Sharqi"
175
- )
176
- else:
177
- return (
178
- f"Executive Summary:\n{name} demonstrates strong educational performance.\n\n"
179
- "Findings:\nTeaching quality and management are satisfactory.\n\n"
180
- "Recommendations:\n1. Conduct monthly internal assessments.\n2. Improve infrastructure.\n\n"
181
- "Conclusion:\nThe institution is progressing effectively.\n\n"
182
- "Developed by Najaf Ali Sharqi"
183
- )
184
-
185
-
186
- def generate_report_from_data(school_data, language):
187
- prompt = build_prompt(school_data, language)
188
  try:
189
- report = generate_with_gemini(prompt)
190
  report = sanitize_text(report)
191
  except Exception:
192
- report = sample_report(school_data, language)
193
-
194
- charts = create_charts(school_data) if language == "English" else []
195
- if language == "English":
196
- file_bytes = create_docx(report, school_data, charts)
197
- fname = f"EPA_Report_{school_data.get('name','school').replace(' ','_')}.docx"
198
- else:
199
- file_bytes = build_urdu_md(report)
200
- fname = f"EPA_Report_{school_data.get('name','school').replace(' ','_')}.md"
201
  return report, fname, file_bytes
202
 
203
 
204
- # === GRADIO UI ===
205
  with gr.Blocks(title="Educational Pre-Audit (EPA) Generator") as demo:
206
  gr.Markdown("## 🏫 Educational Pre-Audit (EPA) Generator\n**Developed by Najaf Ali Sharqi**")
207
 
208
- language = gr.Dropdown(["English", "Urdu"], value="English", label="Select Language")
209
-
210
- file_input = gr.File(label="Upload School Data File (.json or .csv)", file_types=[".json", ".csv"])
211
-
212
- gr.Markdown("### Manual Data Entry")
213
-
214
  with gr.Row():
215
  name = gr.Textbox(label="Institution Name", value="Highlanders Public School and College, Skardu")
216
  principal = gr.Textbox(label="Principal Name", value="Mr. Karim Ali")
@@ -232,27 +198,16 @@ with gr.Blocks(title="Educational Pre-Audit (EPA) Generator") as demo:
232
  policy_gaps = gr.Textbox(label="Policy Gaps", value="HR policy missing; incomplete reports", lines=3)
233
  results_summary = gr.Textbox(label="Results Summary", value="Pass Rate 92%; GPA 3.4", lines=2)
234
 
235
- generate_btn = gr.Button("🚀 Generate Report")
236
  preview = gr.Textbox(label="Preview", lines=20)
237
  download = gr.File(label="Download Report")
238
 
239
  def on_generate(
240
- language, file_input, name, principal, location, level, total_students, total_teachers,
241
- total_non_teaching_staff, observation_average, feedback_average, infrastructure, policy_gaps, results_summary
 
242
  ):
243
- data = {}
244
- if file_input is not None:
245
- ext = os.path.splitext(file_input.name)[1].lower()
246
- try:
247
- if ext == ".json":
248
- data = json.load(open(file_input.name, "r", encoding="utf-8"))
249
- elif ext == ".csv":
250
- df = pd.read_csv(file_input.name)
251
- data = df.to_dict(orient="records")[0]
252
- except Exception:
253
- pass
254
-
255
- data.update({
256
  "name": name,
257
  "principal": principal,
258
  "location": location,
@@ -265,9 +220,9 @@ with gr.Blocks(title="Educational Pre-Audit (EPA) Generator") as demo:
265
  "infrastructure": infrastructure,
266
  "policy_gaps": policy_gaps,
267
  "results": results_summary,
268
- })
269
 
270
- report, fname, fbytes = generate_report_from_data(data, language)
271
  tmp = f"/tmp/{fname}"
272
  with open(tmp, "wb") as f:
273
  f.write(fbytes)
@@ -275,10 +230,10 @@ with gr.Blocks(title="Educational Pre-Audit (EPA) Generator") as demo:
275
 
276
  generate_btn.click(
277
  on_generate,
278
- inputs=[language, file_input, name, principal, location, level, total_students, total_teachers,
279
- total_non_teaching_staff, observation_average, feedback_average, infrastructure, policy_gaps,
280
- results_summary],
281
- outputs=[preview, download]
282
  )
283
 
284
  if __name__ == "__main__":
 
 
1
  """
2
  Educational Pre-Audit (EPA) Generator
3
+ Generates customized English school audit reports (.docx)
4
  Developed by Najaf Ali Sharqi
5
  """
6
 
 
18
  from docx.oxml import OxmlElement
19
  from docx.oxml.ns import qn
20
 
21
+ # Optional: Gemini or Groq API (comment out Gemini if unused)
22
+ from groq import Groq
23
+ client = Groq(api_key="YOUR_GROQ_API_KEY") # 🔑 Replace with your key
 
 
 
 
 
 
 
24
 
25
 
26
  def sanitize_text(text: str) -> str:
27
+ """Remove symbols and emojis."""
28
  return "".join(ch for ch in text if ord(ch) >= 32)
29
 
30
 
31
+ def build_prompt(school_data: dict) -> str:
32
+ return (
33
  "You are EduAudit AI, a professional educational auditor.\n"
34
+ "Generate a detailed Educational Pre-Audit Report with the following structure:\n"
35
  "1. Executive Summary\n2. Domain-wise Findings\n3. Key Findings Table\n"
36
  "4. Recommendations and SMART Action Plan\n5. Overall Audit Rating (1–5)\n6. Conclusion\n\n"
37
+ "Each heading must be bold and start on a new line. Sub-headings may also be bold.\n"
38
+ "The normal text under headings should not be bold.\n"
39
+ "Use clear, professional tone and align with Pakistani educational context.\n\n"
40
+ f"DATA:\n{json.dumps(school_data, ensure_ascii=False, indent=2)}\n"
41
  )
 
42
 
43
 
44
+ def generate_with_groq(prompt: str) -> str:
45
+ """Generate text using Groq API."""
46
+ r = client.chat.completions.create(
47
+ model="llama3-8b-8192",
48
+ messages=[{"role": "user", "content": prompt}],
49
+ temperature=0.6,
50
+ )
51
+ return r.choices[0].message.content.strip()
52
 
53
 
54
  def set_paragraph_format(paragraph, font_size=12, spacing=1.5, bold=False):
55
+ """Applies uniform paragraph formatting."""
56
  for run in paragraph.runs:
57
  run.font.name = "Times New Roman"
58
  run.font.size = Pt(font_size)
 
61
 
62
 
63
  def create_docx(report: str, data: dict, charts: list):
64
+ """Creates a .docx file with bold headings only."""
65
  doc = Document()
66
 
67
+ # Header
68
  section = doc.sections[0]
69
  header = section.header.paragraphs[0]
70
  header.text = "Developed by Najaf Ali Sharqi"
 
76
  set_paragraph_format(title, bold=True)
77
 
78
  # Metadata
79
+ metadata = [
80
+ f"Generated on: {datetime.now():%Y-%m-%d %H:%M}",
81
+ f"Institution: {data.get('name','')}",
82
+ f"Principal: {data.get('principal','')}",
83
+ f"Location: {data.get('location','')}",
84
+ f"Level: {data.get('level','')}",
85
+ ]
86
+ for line in metadata:
87
+ p = doc.add_paragraph(line)
88
  set_paragraph_format(p)
89
 
90
  doc.add_paragraph()
 
96
  cap.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
97
  set_paragraph_format(cap)
98
 
99
+ # --- Text formatting logic ---
100
  lines = report.split("\n")
101
  for line in lines:
102
+ line = line.strip()
103
+ if not line:
104
  continue
105
+
106
+ # Detect headings / subheadings (only bold)
107
+ if (
108
+ line.lower().startswith(("executive summary", "domain-wise", "key findings",
109
+ "recommendations", "overall audit", "conclusion"))
110
+ or line.strip().endswith(":")
111
+ ):
112
+ para = doc.add_paragraph(line)
113
  set_paragraph_format(para, bold=True)
114
+ doc.add_paragraph() # blank line after heading
115
  else:
116
+ para = doc.add_paragraph(line)
117
+ set_paragraph_format(para, bold=False)
 
118
 
119
+ # Footer with page numbers
120
  footer = section.footer.paragraphs[0]
121
+ fld = OxmlElement("w:fldSimple")
122
+ fld.set(qn("w:instr"), "PAGE")
123
  footer._p.append(fld)
124
 
125
+ # Save
126
  bio = io.BytesIO()
127
  doc.save(bio)
128
  bio.seek(0)
 
134
  obs = float(data.get("observation_average", 0))
135
  fb = float(data.get("feedback_average", 0))
136
  fig, ax = plt.subplots(figsize=(5, 3))
137
+ ax.bar(["Observation", "Feedback"], [obs, fb])
138
  ax.set_ylim(0, 5)
139
  ax.set_ylabel("Score (1-5)")
140
  plt.tight_layout()
 
142
  fig.savefig(buf, format="png")
143
  plt.close(fig)
144
  charts.append((buf.getvalue(), "Observation vs Feedback (1–5 scale)"))
 
 
 
 
 
 
 
 
 
 
145
  return charts
146
 
147
 
148
+ def sample_report(data):
149
+ name = data.get("name", "School")
150
+ return (
151
+ f"Executive Summary:\n{name} demonstrates strong performance.\n\n"
152
+ "Domain-wise Findings:\nTeaching quality and management are satisfactory.\n\n"
153
+ "Key Findings Table:\nInfrastructure needs improvement in labs.\n\n"
154
+ "Recommendations and SMART Action Plan:\n"
155
+ "1. Conduct monthly assessments.\n2. Improve classroom facilities.\n\n"
156
+ "Overall Audit Rating (1–5): 4.2\n\n"
157
+ "Conclusion:\nThe institution is performing effectively.\n"
158
+ )
 
159
 
160
 
161
+ def generate_report_from_data(school_data):
162
+ """Main report generation function."""
163
+ prompt = build_prompt(school_data)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  try:
165
+ report = generate_with_groq(prompt)
166
  report = sanitize_text(report)
167
  except Exception:
168
+ report = sample_report(school_data)
169
+
170
+ charts = create_charts(school_data)
171
+ file_bytes = create_docx(report, school_data, charts)
172
+ fname = f"EPA_Report_{school_data.get('name','school').replace(' ','_')}.docx"
 
 
 
 
173
  return report, fname, file_bytes
174
 
175
 
176
+ # === GRADIO INTERFACE ===
177
  with gr.Blocks(title="Educational Pre-Audit (EPA) Generator") as demo:
178
  gr.Markdown("## 🏫 Educational Pre-Audit (EPA) Generator\n**Developed by Najaf Ali Sharqi**")
179
 
 
 
 
 
 
 
180
  with gr.Row():
181
  name = gr.Textbox(label="Institution Name", value="Highlanders Public School and College, Skardu")
182
  principal = gr.Textbox(label="Principal Name", value="Mr. Karim Ali")
 
198
  policy_gaps = gr.Textbox(label="Policy Gaps", value="HR policy missing; incomplete reports", lines=3)
199
  results_summary = gr.Textbox(label="Results Summary", value="Pass Rate 92%; GPA 3.4", lines=2)
200
 
201
+ generate_btn = gr.Button("🚀 Generate English Report")
202
  preview = gr.Textbox(label="Preview", lines=20)
203
  download = gr.File(label="Download Report")
204
 
205
  def on_generate(
206
+ name, principal, location, level, total_students, total_teachers,
207
+ total_non_teaching_staff, observation_average, feedback_average,
208
+ infrastructure, policy_gaps, results_summary
209
  ):
210
+ data = {
 
 
 
 
 
 
 
 
 
 
 
 
211
  "name": name,
212
  "principal": principal,
213
  "location": location,
 
220
  "infrastructure": infrastructure,
221
  "policy_gaps": policy_gaps,
222
  "results": results_summary,
223
+ }
224
 
225
+ report, fname, fbytes = generate_report_from_data(data)
226
  tmp = f"/tmp/{fname}"
227
  with open(tmp, "wb") as f:
228
  f.write(fbytes)
 
230
 
231
  generate_btn.click(
232
  on_generate,
233
+ inputs=[name, principal, location, level, total_students, total_teachers,
234
+ total_non_teaching_staff, observation_average, feedback_average,
235
+ infrastructure, policy_gaps, results_summary],
236
+ outputs=[preview, download],
237
  )
238
 
239
  if __name__ == "__main__":