musawar32ali commited on
Commit
1e2a4ce
·
verified ·
1 Parent(s): f229a97

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +316 -48
app.py CHANGED
@@ -1,64 +1,332 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
2
  from docx import Document
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
 
4
- def generate_timetable(subjects, teachers, rooms):
5
- subjects = subjects.split("\n")
6
- teachers = teachers.split("\n")
7
- rooms = rooms.split("\n")
8
-
9
- timetable = []
10
- for i in range(len(subjects)):
11
- sub = subjects[i] if i < len(subjects) else ""
12
- tch = teachers[i] if i < len(teachers) else ""
13
- rm = rooms[i] if i < len(rooms) else ""
14
- timetable.append([i+1, sub, tch, rm])
15
-
16
- # Create TXT
17
- txt_content = "Period | Subject | Teacher | Room\n"
18
- txt_content += "-----------------------------------------\n"
19
- for row in timetable:
20
- txt_content += f"{row[0]} | {row[1]} | {row[2]} | {row[3]}\n"
21
- with open("timetable.txt", "w") as f:
22
- f.write(txt_content)
23
-
24
- # Create DOCX
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  doc = Document()
26
- doc.add_heading("Automatic Timetable", level=1)
27
- table = doc.add_table(rows=1, cols=4)
28
- hdr_cells = table.rows[0].cells
29
- hdr_cells[0].text = "Period"
30
- hdr_cells[1].text = "Subject"
31
- hdr_cells[2].text = "Teacher"
32
- hdr_cells[3].text = "Room"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
- for row in timetable:
35
- row_cells = table.add_row().cells
36
- row_cells[0].text = str(row[0])
37
- row_cells[1].text = row[1]
38
- row_cells[2].text = row[2]
39
- row_cells[3].text = row[3]
 
 
 
 
 
 
 
 
 
 
 
 
40
 
41
- doc.save("timetable.docx")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
- return timetable, "timetable.docx", "timetable.txt"
 
 
 
 
 
 
44
 
 
 
 
45
 
46
- # ---- UI (PDF REMOVED, STYLE UNCHANGED) ----
 
47
 
48
- with gr.Blocks() as demo:
49
- gr.Markdown("<h1 style='text-align:center;'>Automatic Time Table Generator</h1>")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
 
51
  with gr.Row():
52
- subjects = gr.Textbox(label="Enter Subjects (line by line)")
53
- teachers = gr.Textbox(label="Enter Teacher Names (line by line)")
54
- rooms = gr.Textbox(label="Enter Rooms (line by line)")
 
 
 
 
 
 
 
55
 
56
- generate_btn = gr.Button("Generate Timetable")
57
- output = gr.Dataframe(headers=["Period", "Subject", "Teacher", "Room"])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
- docx_out = gr.File(label="Download Word File")
60
- txt_out = gr.File(label="Download Text File")
 
 
 
61
 
62
- generate_btn.click(generate_timetable, [subjects, teachers, rooms], [output, docx_out, txt_out])
 
 
 
 
63
 
64
- demo.launch()
 
 
1
+ """
2
+ Automatic Time Table Generator (Improved)
3
+ - Gradio app for Hugging Face Spaces
4
+ - Exports: Word (.docx) and Text (.txt)
5
+ - Sunday = Holiday
6
+ - Languages: English, Sindhi, Urdu
7
+ """
8
+
9
+ import io
10
+ import tempfile
11
+ import random
12
+ from typing import List, Dict
13
  import gradio as gr
14
  from docx import Document
15
+ from datetime import time, timedelta, datetime
16
+
17
+ # -------------------
18
+ # Localization labels
19
+ # -------------------
20
+ LANG = {
21
+ "English": {
22
+ "app_title": "Automatic Time Table Generator",
23
+ "subtitle": "Generate timetables with subjects, teachers, rooms and period times.",
24
+ "days_label": "Working days (comma-separated, omit Sunday; Sunday will be shown as Holiday)",
25
+ "subjects_label": "Subjects (comma-separated, e.g. Math, English, Science)",
26
+ "teachers_label": "Teachers (comma-separated, will cycle if fewer than subjects)",
27
+ "rooms_label": "Rooms (comma-separated or single numbers, e.g. 1,2,3 ... 10)",
28
+ "classes_label": "Class list (comma-separated, e.g. 1,2,3,4,5)",
29
+ "periods_label": "Periods per day (integer)",
30
+ "duration_label": "Period duration (hours, integer)",
31
+ "start_label": "Start time (24h, e.g. 8 for 08:00)",
32
+ "generate_btn": "Generate & Export (Word / Text)",
33
+ "preview_label": "Generated Timetable Preview",
34
+ "holiday_text": "Holiday",
35
+ "word_label": "Download Word (.docx)",
36
+ "text_label": "Download Text (.txt)",
37
+ "doc_title": "Timetable"
38
+ },
39
+ "Sindhi": {
40
+ "app_title": "خودڪار ٽائيم ٽيبل جنريٽر",
41
+ "subtitle": "مضمون، استادن، ڪمري ۽ دورانيه سان ٽائيم ٽيبل ٺاهيو.",
42
+ "days_label": "ڪم وارا ڏينهن (ڪاما سان الڳ، آچر شامل نه ڪريو؛ آچر موڪل ڏيکاريو ويندو)",
43
+ "subjects_label": "مضمون (ڪاما سان الڳ ڪريو، مثال: Math, English, Science)",
44
+ "teachers_label": "استاد (ڪاما سان الڳ ڪريو، جيڪڏهن گهٽ هجن ته ورجايا ويندا)",
45
+ "rooms_label": "ڪمره (ڪاما سان يا انگ، مثال: 1,2,3 ... 10)",
46
+ "classes_label": "ڪلاس لسٽ (ڪاما سان، مثال: 1,2,3,4,5)",
47
+ "periods_label": "في ڏينهن دورا (عدد)",
48
+ "duration_label": "هر دورو (گهنٽا)",
49
+ "start_label": "شروعات جو وقت (24h، مثال: 8 لاءِ 08:00)",
50
+ "generate_btn": "جنريٽ ۽ ٻاھ ڪڍو (Word / Text)",
51
+ "preview_label": "پريوئو",
52
+ "holiday_text": "موڪل",
53
+ "word_label": "Word (.docx) ڊائونلوڊ ڪريو",
54
+ "text_label": "Text (.txt) ڊائونلوڊ ڪريو",
55
+ "doc_title": "ٽائيم ٽيبل"
56
+ },
57
+ "Urdu": {
58
+ "app_title": "خودکار ٹائم ٹیبل جنریٹر",
59
+ "subtitle": "مضامین، اساتذہ، کمروں اور دورانیوں کے ساتھ جدول بنائیں۔",
60
+ "days_label": "کام کے دن (کاما سے جدا کریں، اتوار شامل نہ کریں؛ اتوار تعطیل دکھایا جائے گا)",
61
+ "subjects_label": "مضامین (کاما سے جدا کریں، مثال: Math, English, Science)",
62
+ "teachers_label": "اساتذہ (کاما سے جدا کریں، اگر کم ہوں گے تو دوبارہ آئیں گے)",
63
+ "rooms_label": "کمروں کے نمبر (کاما سے یا نمبر، مثال: 1,2,3 ... 10)",
64
+ "classes_label": "کلاس فہرست (کاما سے، مثال: 1,2,3,4,5)",
65
+ "periods_label": "روزانہ دورانیے (عدد)",
66
+ "duration_label": "ہر دورانیہ (گھنٹے)",
67
+ "start_label": "شروع وقت (24h، مثال: 8 = 08:00)",
68
+ "generate_btn": "جنریٹ کریں اور ایکسپورٹ کریں (Word / Text)",
69
+ "preview_label": "پریویو",
70
+ "holiday_text": "چھٹی",
71
+ "word_label": "Word (.docx) ڈاؤن لوڈ کریں",
72
+ "text_label": "Text (.txt) ڈاؤن لوڈ کریں",
73
+ "doc_title": "ٹائم ٹیبل"
74
+ }
75
+ }
76
+
77
+ HOLIDAY = "Sunday"
78
+
79
+ # -------------------
80
+ # Utilities
81
+ # -------------------
82
+ def parse_csv(s: str) -> List[str]:
83
+ return [p.strip() for p in s.split(",") if p.strip()]
84
+
85
+ def ensure_days(days: List[str]) -> List[str]:
86
+ # Normalize and ensure Sunday at end
87
+ cleaned = []
88
+ for d in days:
89
+ d2 = d.strip().capitalize()
90
+ if d2 and d2.lower() != HOLIDAY.lower():
91
+ if d2 not in cleaned:
92
+ cleaned.append(d2)
93
+ cleaned.append(HOLIDAY)
94
+ return cleaned
95
+
96
+ def compute_period_times(start_hour: int, duration_hours: int, periods: int) -> List[str]:
97
+ times = []
98
+ current = datetime(2000,1,1, start_hour, 0)
99
+ delta = timedelta(hours=duration_hours)
100
+ for _ in range(periods):
101
+ end = current + delta
102
+ times.append(f"{current.strftime('%H:%M')} - {end.strftime('%H:%M')}")
103
+ current = end
104
+ return times
105
+
106
+ # -------------------
107
+ # Timetable generation
108
+ # -------------------
109
+ def generate_timetable_for_class(days, subjects, teachers, rooms, period_times):
110
+ """
111
+ returns: dict day -> list of {time, subject, teacher, room}
112
+ """
113
+ mapping = {}
114
+ # ensure rooms list has at least 1 item
115
+ if not rooms:
116
+ rooms = ["Room1"]
117
+ if not teachers:
118
+ teachers = ["TBD"]
119
+ if not subjects:
120
+ subjects = ["Free"]
121
 
122
+ for day in days:
123
+ if day.lower() == HOLIDAY.lower():
124
+ mapping[day] = [] # Holiday marker
125
+ continue
126
+
127
+ # shuffle subjects for uniqueness
128
+ order = subjects.copy()
129
+ random.shuffle(order)
130
+
131
+ day_slots = []
132
+ for idx, t in enumerate(period_times):
133
+ subj = order[idx % len(order)]
134
+ teacher = teachers[idx % len(teachers)]
135
+ room = rooms[idx % len(rooms)]
136
+ day_slots.append({
137
+ "time": t,
138
+ "subject": subj,
139
+ "teacher": teacher,
140
+ "room": room
141
+ })
142
+ mapping[day] = day_slots
143
+ return mapping
144
+
145
+ def generate_full_timetable(class_list, days, subjects, teachers, rooms, period_times):
146
+ full = {}
147
+ for cls in class_list:
148
+ full[cls] = generate_timetable_for_class(days, subjects, teachers, rooms, period_times)
149
+ return full
150
+
151
+ # -------------------
152
+ # Export helpers
153
+ # -------------------
154
+ def export_docx_bytes(full_tt: Dict[str, Dict], labels) -> str:
155
+ """
156
+ Create a temporary .docx file and return its path
157
+ """
158
  doc = Document()
159
+ doc.add_heading(labels["doc_title"], level=0)
160
+
161
+ for cls, tt in full_tt.items():
162
+ doc.add_heading(f"Class {cls}", level=1)
163
+ for day, slots in tt.items():
164
+ doc.add_heading(day, level=2)
165
+ if not slots:
166
+ doc.add_paragraph(labels["holiday_text"])
167
+ else:
168
+ table = doc.add_table(rows=1, cols=4)
169
+ hdr = table.rows[0].cells
170
+ hdr[0].text = "Time"
171
+ hdr[1].text = "Subject"
172
+ hdr[2].text = "Teacher"
173
+ hdr[3].text = "Room"
174
+ for s in slots:
175
+ row = table.add_row().cells
176
+ row[0].text = s["time"]
177
+ row[1].text = s["subject"]
178
+ row[2].text = s["teacher"]
179
+ row[3].text = s["room"]
180
+ doc.add_paragraph("") # small space
181
+
182
+ tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".docx")
183
+ doc.save(tmp.name)
184
+ tmp.close()
185
+ return tmp.name
186
 
187
+ def export_text_bytes(full_tt: Dict[str, Dict], labels) -> str:
188
+ lines = []
189
+ for cls, tt in full_tt.items():
190
+ lines.append(f"Class {cls}")
191
+ lines.append("-" * (8 + len(str(cls))))
192
+ for day, slots in tt.items():
193
+ lines.append(f"{day}:")
194
+ if not slots:
195
+ lines.append(f" {labels['holiday_text']}")
196
+ else:
197
+ for s in slots:
198
+ lines.append(f" {s['time']} | {s['subject']} | {s['teacher']} | Room: {s['room']}")
199
+ lines.append("")
200
+ txt = "\n".join(lines)
201
+ tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".txt", mode="w", encoding="utf-8")
202
+ tmp.write(txt)
203
+ tmp.close()
204
+ return tmp.name
205
 
206
+ # -------------------
207
+ # Main Gradio function
208
+ # -------------------
209
+ def build_and_export(
210
+ language: str,
211
+ days_input: str,
212
+ subjects_input: str,
213
+ teachers_input: str,
214
+ rooms_input: str,
215
+ classes_input: str,
216
+ periods_per_day: int,
217
+ duration_hours: int,
218
+ start_hour: int,
219
+ export_choice: str
220
+ ):
221
+ labels = LANG.get(language, LANG["English"])
222
 
223
+ # parse inputs
224
+ days = parse_csv(days_input)
225
+ days = ensure_days(days)
226
+ subjects = [s.strip() for s in parse_csv(subjects_input)]
227
+ teachers = [t.strip() for t in parse_csv(teachers_input)]
228
+ rooms = [r.strip() for r in parse_csv(rooms_input)] or [f"Room{i}" for i in range(1, 11)]
229
+ class_list = [c.strip() for c in parse_csv(classes_input)] or ["1","2","3","4","5"]
230
 
231
+ # validation
232
+ if periods_per_day <= 0:
233
+ return "⚠️ Periods per day must be a positive integer.", None, None
234
 
235
+ if duration_hours <= 0:
236
+ return "⚠️ Period duration must be a positive integer (hours).", None, None
237
 
238
+ if start_hour < 0 or start_hour > 23:
239
+ return "⚠️ Start hour must be between 0 and 23.", None, None
240
+
241
+ period_times = compute_period_times(start_hour, duration_hours, periods_per_day)
242
+
243
+ full_tt = generate_full_timetable(class_list, days, subjects, teachers, rooms, period_times)
244
+
245
+ # Build preview text
246
+ preview_lines = []
247
+ for cls, tt in full_tt.items():
248
+ preview_lines.append(f"Class {cls}")
249
+ preview_lines.append("-" * (8 + len(str(cls))))
250
+ for day, slots in tt.items():
251
+ preview_lines.append(f"{day}:")
252
+ if not slots:
253
+ preview_lines.append(f" {labels['holiday_text']}")
254
+ else:
255
+ for s in slots:
256
+ preview_lines.append(f" {s['time']} | {s['subject']} | {s['teacher']} | Room: {s['room']}")
257
+ preview_lines.append("")
258
+ preview_text = "\n".join(preview_lines)
259
+
260
+ # Exports
261
+ docx_path = None
262
+ txt_path = None
263
+ if export_choice in ("Word", "Both"):
264
+ docx_path = export_docx_bytes(full_tt, labels)
265
+ if export_choice in ("Text", "Both"):
266
+ txt_path = export_text_bytes(full_tt, labels)
267
+
268
+ # return preview and file paths (Gradio File component accepts a filepath)
269
+ # If a particular export wasn't requested, return None for its file output
270
+ return preview_text, docx_path, txt_path
271
+
272
+ # -------------------
273
+ # Gradio UI
274
+ # -------------------
275
+ with gr.Blocks(title="Automatic Time Table Generator (Improved)") as demo:
276
+ # Header
277
+ with gr.Row():
278
+ lang = gr.Dropdown(list(LANG.keys()), value="English", label="Language")
279
+ gr.Markdown("<h2 style='margin-left:12px'>Automatic Time Table Generator (Improved)</h2>")
280
+
281
+ with gr.Row():
282
+ days_in = gr.Textbox(value="Monday, Tuesday, Wednesday, Thursday, Friday, Saturday", label=LANG["English"]["days_label"])
283
+ classes_in = gr.Textbox(value="1,2,3,4,5", label=LANG["English"]["classes_label"])
284
+
285
+ subjects_in = gr.Textbox(value="Math, English, Science, History, Art", label=LANG["English"]["subjects_label"])
286
+ teachers_in = gr.Textbox(value="Mr. Ali, Ms. Fatima, Mr. Khan, Ms. Sara", label=LANG["English"]["teachers_label"])
287
+ rooms_in = gr.Textbox(value="1,2,3,4,5,6,7,8,9,10", label=LANG["English"]["rooms_label"])
288
 
289
  with gr.Row():
290
+ periods_in = gr.Number(value=6, label=LANG["English"]["periods_label"])
291
+ duration_in = gr.Number(value=1, label=LANG["English"]["duration_label"])
292
+ start_in = gr.Number(value=8, label=LANG["English"]["start_label"])
293
+
294
+ export_choice = gr.Dropdown(choices=["Word", "Text", "Both"], value="Both", label="Export option")
295
+ generate_btn = gr.Button(LANG["English"]["generate_btn"], variant="primary")
296
+
297
+ preview = gr.Textbox(label=LANG["English"]["preview_label"], lines=20, interactive=False)
298
+ download_docx = gr.File(label=LANG["English"]["word_label"])
299
+ download_txt = gr.File(label=LANG["English"]["text_label"])
300
 
301
+ # When language changes, update labels (client-side)
302
+ def update_ui_labels(selected_lang):
303
+ labels = LANG.get(selected_lang, LANG["English"])
304
+ return (
305
+ gr.update(label=labels["days_label"]),
306
+ gr.update(label=labels["subjects_label"]),
307
+ gr.update(label=labels["teachers_label"]),
308
+ gr.update(label=labels["rooms_label"]),
309
+ gr.update(label=labels["classes_label"]),
310
+ gr.update(label=labels["periods_label"]),
311
+ gr.update(label=labels["duration_label"]),
312
+ gr.update(label=labels["start_label"]),
313
+ gr.update(value=labels["generate_btn"]),
314
+ gr.update(label=labels["preview_label"]),
315
+ gr.update(label=labels["word_label"]),
316
+ gr.update(label=labels["text_label"])
317
+ )
318
 
319
+ lang.change(
320
+ fn=update_ui_labels,
321
+ inputs=[lang],
322
+ outputs=[days_in, subjects_in, teachers_in, rooms_in, classes_in, periods_in, duration_in, start_in, generate_btn, preview, download_docx, download_txt]
323
+ )
324
 
325
+ generate_btn.click(
326
+ fn=build_and_export,
327
+ inputs=[lang, days_in, subjects_in, teachers_in, rooms_in, classes_in, periods_in, duration_in, start_in, export_choice],
328
+ outputs=[preview, download_docx, download_txt]
329
+ )
330
 
331
+ if __name__ == "__main__":
332
+ demo.launch(server_name="0.0.0.0", share=False)