Yinxing commited on
Commit
cf2448f
·
verified ·
1 Parent(s): 128f7f9

Upload 授業負担計算後期.py

Browse files
Files changed (1) hide show
  1. 授業負担計算後期.py +665 -0
授業負担計算後期.py ADDED
@@ -0,0 +1,665 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import numpy as np
3
+ import pandas as pd
4
+ import mojimoji
5
+
6
+
7
+
8
+ def get_kamoku(df):
9
+ daigakuin = pd.read_excel(df,sheet_name='リサーチコース特論等', skiprows=5)
10
+ gakubu = pd.read_excel(df,sheet_name='学部', skiprows=4)
11
+ tokusyu = pd.read_excel(df,sheet_name='特殊講義', skiprows=5)
12
+ gpem = pd.read_excel(df,sheet_name='GPEM特論等', skiprows=4)
13
+
14
+ d_data = jyugyo_daigakuin(daigakuin)
15
+ u_data = jyugyo(gakubu, drop='Vlookup元')
16
+ gpem_data = gpem_jyugyo(gpem)
17
+ tokusyu_data = tokusyu_jyugyo(tokusyu)
18
+
19
+ all = u_data + d_data + gpem_data + tokusyu_data
20
+ df = pd.DataFrame(all, columns=['時限', '場所', '授業名', '担当教員', '授業区分','Code'])
21
+
22
+ # 重複している講義を検索
23
+ time_unique = df['時限'].unique()
24
+ time_unique.sort()
25
+ lectures = pd.DataFrame(columns=['特論','GPEM', '特集講義','時限','場所','担当教員','特論_code','Gpem_code', '特殊講義_code'])
26
+ lecture_list = []
27
+ for i in time_unique:
28
+ sub = df[df['時限'] == i]
29
+ teacher_unique = sub['担当教員'].unique()
30
+ basyo_unique = sub['場所'].unique()
31
+ #display(i)
32
+ #display(sub)
33
+ for j in basyo_unique:
34
+ sub2 = sub[sub['場所'] == j]
35
+ sub2 = sub2[sub2['授業区分'] != '学部講義']
36
+ if len(sub2) > 0:
37
+ teacher = sub2.iloc[0,3]
38
+ time = sub2.iloc[0,0]
39
+ basyo = sub2.iloc[0,1]
40
+ #display(sub2)
41
+ if sub2[sub2['授業区分'] == '特論'].shape[0] > 0:
42
+ tokuron = sub2[sub2['授業区分'] == '特論'].iloc[0,2]
43
+ tokuron_code = sub2[sub2['授業区分'] == '特論'].iloc[0,5]
44
+ teacher = sub2.iloc[0,3]
45
+ else:
46
+ tokuron = ''
47
+ if sub2[sub2['授業区分'] == 'GPEM'].shape[0] > 0:
48
+ gpem_ = sub2[sub2['授業区分'] == 'GPEM'].iloc[0,2]
49
+ gpem_code = sub2[sub2['授業区分'] == 'GPEM'].iloc[0,5]
50
+ else:
51
+ gpem_ = ''
52
+ if sub2[sub2['授業区分'] == '特集講義'].shape[0] > 0:
53
+ tokusyu_ = sub2[sub2['授業区分'] == '特集講義'].iloc[0,2]
54
+ tokusyu_code = sub2[sub2['授業区分'] == '特集講義'].iloc[0,5]
55
+ else:
56
+ tokusyu_ = ''
57
+ tokusyu_code = ''
58
+
59
+ lecture_list.append([tokuron, gpem_, tokusyu_, time, basyo, teacher, tokuron_code, gpem_code, tokusyu_code])
60
+
61
+
62
+ lectures = pd.DataFrame(lecture_list, columns=['特論','GPEM', '特集講義','時限','場所','担当教員','特論_code','Gpem_code', '特殊講義_code'])
63
+ #lectures.to_excel('大学院講義一覧.xlsx', index=False)
64
+
65
+ return lectures
66
+
67
+ def jyugyo_daigakuin(df, drop='Vlookup用.1'):
68
+ df2 = df.copy()
69
+ df2.drop(drop,axis=1, inplace=True)
70
+ span = 7
71
+ data = []
72
+ for i in range(6):
73
+ cols = range(i*span+1, i*span+span+1)
74
+ sub = df2.iloc[:, cols].values
75
+ for j in sub:
76
+ if j[0] is np.nan:
77
+ break # 表まで
78
+
79
+ # 空欄ではない授業のみ
80
+ if j[2] is not np.nan:
81
+ #print(j[0], j[2], j[3])
82
+ # 月/前期の4文字のみ + 時限
83
+ jigen = j[0][:5] + f'{i+1}'
84
+ basyo = j[4]
85
+ name = j[2]
86
+ teacher = j[6]
87
+ data.append([jigen, basyo, name, teacher, '特論', j[1]])
88
+
89
+ return data
90
+
91
+
92
+ # 学部
93
+ def jyugyo(df, drop='Vlookup元'):
94
+ df2 = df.copy()
95
+ df2.drop(drop,axis=1, inplace=True)
96
+ span = 8
97
+ data = []
98
+ for i in range(6):
99
+ cols = range(i*span+1, i*span+span+1)
100
+ sub = df2.iloc[:, cols].values
101
+ for j in sub:
102
+ if j[0] is np.nan:
103
+ break # 表まで
104
+
105
+ # 空欄ではない授業のみ
106
+ if j[2] is not np.nan:
107
+ #print(j[0], j[2], j[3])
108
+ # 月/前期の4文字のみ + 時限
109
+ jigen = j[0][:5] + f'{i+1}'
110
+ basyo = j[5]
111
+ name = j[2]
112
+ teacher = j[7]
113
+ data.append([jigen, basyo, name, teacher,'学部講義', j[1]])
114
+
115
+ return data
116
+
117
+ # 学部
118
+ def gpem_jyugyo(df, drop='Vlookup用.1'):
119
+ df2 = df.copy()
120
+ df2.drop(drop,axis=1, inplace=True)
121
+ span = 7
122
+ data = []
123
+ for i in range(6):
124
+ cols = range(i*span+1, i*span+span+1)
125
+ sub = df2.iloc[:, cols].values
126
+ for j in sub:
127
+ if j[0] is np.nan:
128
+ break # 表まで
129
+
130
+ # 空欄ではない授業のみ
131
+ if j[2] is not np.nan:
132
+ #print(j[0], j[2], j[3])
133
+ # 月/前期の4文字のみ + 時限
134
+ jigen = j[0][:5] + f'{i+1}'
135
+ basyo = j[4]
136
+ name = j[2]
137
+ teacher = j[6]
138
+ data.append([jigen, basyo, name, teacher, 'GPEM', j[1]])
139
+ #display(df2.iloc[:50, cols])
140
+ return data
141
+
142
+ # 特集講義
143
+ def tokusyu_jyugyo(df, drop='Vlookup用.1'):
144
+ df2 = df.copy()
145
+ df2.drop(drop,axis=1, inplace=True)
146
+ span = 7
147
+ data = []
148
+ for i in range(6):
149
+ cols = range(i*span+1, i*span+span+1)
150
+ sub = df2.iloc[:, cols].values
151
+ for j in sub:
152
+ if j[0] is np.nan:
153
+ break # 表まで
154
+
155
+ # 空欄ではない授業のみ
156
+ if j[2] is not np.nan:
157
+ #print(j[0], j[2], j[3])
158
+ # 月/前期の4文字のみ + 時限
159
+ jigen = j[0][:5] + f'{i+1}'
160
+ basyo = j[4]
161
+ name = j[2]
162
+ teacher = j[6]
163
+ data.append([jigen, basyo, name, teacher, '特集講義', j[1]])
164
+ #display(df2.iloc[:, cols])
165
+ return data
166
+
167
+
168
+
169
+ # 名前の\u3000を空白に変換
170
+ def convert(df):
171
+ # df: pandas.Series
172
+ return df.str.replace('\u3000', ' ')
173
+
174
+ def convert_fullwidth_to_halfwidth(df):
175
+ # df: pandas.Series
176
+ return df.apply(lambda x: mojimoji.zen_to_han(x, kana=False))
177
+
178
+
179
+
180
+ def get_data(df):
181
+ kyouin = pd.read_excel(df, sheet_name='教員名簿')
182
+ return kyouin
183
+
184
+
185
+
186
+ def main_func(d1, d2, d3, d4, d5, d6=None):
187
+ kougi_list = get_kamoku(d1)
188
+ kyouin = pd.read_excel(d2, sheet_name='教員名簿')
189
+ kyouin.set_index('教員名', inplace=True)
190
+ gakubu = pd.read_excel(d3)
191
+ daigakuin = pd.read_excel(d4)
192
+ zenki = pd.read_excel(d5)
193
+
194
+ tune_df = pd.read_excel(d2, sheet_name='科目調整')
195
+ zengaku_df = pd.read_excel(d2, sheet_name='全学講義')
196
+ ng_df = pd.read_excel(d2, sheet_name='対象外科目')
197
+
198
+
199
+ # 除外科目
200
+ NGlecture = ng_df['科目名'].tolist()
201
+ NGlecture += '経営原理,Workshop,ワークショップ'.split(',')
202
+ #display(NGlecture)
203
+ names = list(kyouin.index)
204
+ seminar = ['Seminar', '演習', 'Project Guidance']
205
+
206
+
207
+
208
+
209
+ tmp = []
210
+ for i in kougi_list.iloc[:,:2].values:
211
+ # if not np.nan
212
+ if i[0] is not np.nan:
213
+ t1 = mojimoji.zen_to_han(i[0], kana=False)
214
+ else:
215
+ t1 = np.nan
216
+ if i[1] is not np.nan:
217
+ t2 = mojimoji.zen_to_han(i[1], kana=False)
218
+ else:
219
+ t2 = np.nan
220
+ tmp.append([t1, t2])
221
+
222
+ kougi_list.iloc[:,:2] = tmp
223
+
224
+ all = pd.concat([gakubu, daigakuin], axis=0)
225
+
226
+ all['教員氏名'] = convert(all['教員氏名'])
227
+
228
+ all['科目名称'] = convert_fullwidth_to_halfwidth(all['科目名称'])
229
+
230
+ # まとめ
231
+ matome = []
232
+ lecture_codes = set(all['授業コード'])
233
+ for code in lecture_codes:
234
+ sub = all[all['授業コード'] == code].iloc[0]
235
+ teachers = all[all['授業コード'] == code]['教員氏名'].unique()
236
+ for teacher in teachers:
237
+ if teacher not in names: # 教員名簿にない場合はスルー
238
+ continue
239
+ kana = kyouin.loc[teacher]['かな']
240
+ #display(sub)
241
+ jyugyou = sub['科目名称']
242
+ if sub['授業管理部署名称'] == '経済学部':
243
+ bunrui = '学部'
244
+ else:
245
+ bunrui = '大学院'
246
+ for col in seminar:
247
+ if col in jyugyou:
248
+ bunrui += '演習'
249
+ break
250
+
251
+ tani = sub['単位数']
252
+ count = sub['合計']
253
+
254
+ # GPEM判定: 講義リストにGPEMコードと同じ講義がある場合
255
+ match_kougi = kougi_list[kougi_list['特論_code'] == code]
256
+ match_gpem = kougi_list[kougi_list['Gpem_code'] == code]
257
+ match_tokusyu = kougi_list[kougi_list['特殊講義_code'] == code]
258
+
259
+ biko = ''
260
+ gpem = ''
261
+ if len(match_kougi) > 0:
262
+ if match_kougi.iloc[0, 1] is not np.nan:
263
+ gpem = 'GPEM'
264
+ else:
265
+ gpem = ''
266
+ elif len(match_gpem) > 0:
267
+ gpem = 'GPEM'
268
+ if match_gpem.iloc[0, 0] is not np.nan:
269
+ biko = f'{match_gpem.iloc[0, 0]}と同じ'
270
+ elif len(match_tokusyu) > 0:
271
+ if match_tokusyu.iloc[0, 1] is not np.nan:
272
+ gpem = 'GPEM'
273
+ else:
274
+ gpem = ''
275
+ if match_tokusyu.iloc[0, 0] is not np.nan:
276
+ biko = f'{match_tokusyu.iloc[0, 0]}と同じ'
277
+
278
+
279
+ # 備考
280
+
281
+
282
+ matome.append([kana, teacher, jyugyou, bunrui, gpem, count, tani, biko])
283
+
284
+
285
+
286
+ zenki_moto_data = pd.DataFrame(matome,columns=['かな', '教員名', '科目名', '分類', 'GPEM', '受講者数', '単位数', '備考'])
287
+
288
+
289
+ # ソート
290
+
291
+ # 分類の優先順
292
+ category_order = ['学部', '大学院', '学部演習', '大学院演習']
293
+
294
+ df = zenki_moto_data.copy()
295
+ # 教員名と分類をカテゴリとして順序を定義
296
+ df['教員名'] = pd.Categorical(df['教員名'], categories=names, ordered=True)
297
+ df['分類'] = pd.Categorical(df['分類'], categories=category_order, ordered=True)
298
+
299
+ # ソート実行
300
+ df_sorted = df.sort_values(by=['教員名', '分類'])
301
+
302
+
303
+ columns = 'かな,教員名(敬称略),GPEM,2025年度担当単位数(演習除く),年平均の標準負担単位数(演習除く),受講者ゼロ調整,その他調整,2025年度前期における過不足,備考1,備考2'.split(',')
304
+
305
+
306
+
307
+ kabusoku = pd.DataFrame(columns=columns)
308
+
309
+ for i in names:
310
+ sub = df_sorted[df_sorted['教員名'] == i]
311
+
312
+ # 優先順位:
313
+ # 1. 優先負担単位数があれば、それを優先
314
+ # 2. GPEM教員は5, それ以外は6
315
+
316
+ if not np.isnan(kyouin.loc[i]['優先負担単位数']) :
317
+ #display(kyouin.loc[i]['優先負担単位数'])
318
+ need = int(kyouin.loc[i]['優先負担単位数'])
319
+ else:
320
+ if kyouin.loc[i]['GPEM'] == 'GPEM':
321
+ need = 5
322
+ else:
323
+ need = 6
324
+
325
+ # 分類に「演習」という文字列がないデータを集める
326
+ sub = sub[~sub['分類'].str.contains('演習')]
327
+ # 特殊講義は除外
328
+ sub = sub[~sub['科目名'].str.contains('特殊講義')]
329
+ # 除外科目を除去
330
+ for col in NGlecture:
331
+ sub = sub[~sub['科目名'].str.contains(col)]
332
+
333
+
334
+
335
+ # 同じ授業(特論 + GPEM)の場合は除外
336
+ # 備考列に'同じ'という文字列が含まれている場合は除外
337
+ sub = sub[~sub['備考'].str.contains('同じ')]
338
+
339
+
340
+ scores = 0
341
+ # 備考1 科目名 + 単位数
342
+ biko1 = ''
343
+ extra = ''
344
+ first = True
345
+ for j in sub.values:
346
+ subject = j[2]
347
+ score = int(j[6])
348
+
349
+ # 科目調整があるかチェック
350
+ if tune_df[tune_df['科目名'] == subject].shape[0] > 0:
351
+ score = int(tune_df[tune_df['科目名'] == subject]['単位数'].values[0])
352
+ biko1 += f' + {subject}({score}単位)'
353
+ extra = tune_df[tune_df['科目名'] == subject]['備考'].values[0]
354
+
355
+ scores += score
356
+
357
+ if not first:
358
+ biko1 += f' + {subject}({score}単位)'
359
+ else:
360
+ biko1 = f'{subject}({score}単位)'
361
+ first = False
362
+
363
+
364
+ # 全学講義
365
+ zengaku_sub = zengaku_df[zengaku_df['教員名'] == i]
366
+
367
+ if zengaku_sub.shape[0] > 0:
368
+ for col in NGlecture:
369
+ zengaku_sub = zengaku_sub[~zengaku_sub['授業名'].str.contains(col)]
370
+ for j in zengaku_sub.values:
371
+ # 受講者数が0の場合はスキップ
372
+ if j[3] == 0:
373
+ continue
374
+ subject = j[1]
375
+ score = int(j[2])
376
+ if not first:
377
+ biko1 += f' + {subject}({score}単位)'
378
+ else:
379
+ biko1 = f'{subject}({score}単位)'
380
+
381
+ scores += score
382
+
383
+ if biko1 == '':
384
+ biko1 = '演習のみ'
385
+ biko1 += ';'
386
+
387
+ # 備考2
388
+ if isinstance(kyouin.loc[i]['優先負担単位理由'], float):
389
+ tmp = ''
390
+ else:
391
+ tmp = kyouin.loc[i]['優先負担単位理由']
392
+
393
+
394
+ biko2 = tmp
395
+
396
+ if isinstance(kyouin.loc[i]['その他調整単位理由'], float):
397
+ tmp = ''
398
+ else:
399
+ tmp = kyouin.loc[i]['その他調整単位理由']
400
+
401
+
402
+ # その他調整単位数
403
+ if not np.isnan(kyouin.loc[i]['その他調整単位数']):
404
+ other = kyouin.loc[i]['その他調整単位数']
405
+ else:
406
+ other = 0
407
+
408
+ biko2 += tmp
409
+ if biko2 == '':
410
+ biko2 = extra
411
+ else:
412
+ biko2 += f'; {extra}'
413
+
414
+
415
+
416
+ # 不足: 実際担当 - 標準担当 - ゼロ調整 + その他調整
417
+ husoku = 0 #scores - need - 0 + other # 後で計算
418
+
419
+ kabusoku.loc[len(kabusoku)] = [kyouin.loc[i]['かな'], i, kyouin.loc[i]['GPEM'], scores, need, 0, other, husoku, biko1, biko2]
420
+
421
+
422
+ # 受講者ゼロ調整
423
+ totals = []
424
+ for i in kougi_list.iterrows():
425
+ # 特論 + Gpem + 特集講義 合わせて0を探す
426
+ codes = set()
427
+ if i[1]['特論_code'] != '':
428
+ codes.add(i[1]['特論_code'])
429
+ if i[1]['Gpem_code'] != '':
430
+ codes.add(i[1]['Gpem_code'])
431
+ if i[1]['特殊講義_code'] != '':
432
+ codes.add(i[1]['特殊講義_code'])
433
+ total = 0
434
+ for code in codes:
435
+ daigakuin_sub = daigakuin[daigakuin['授業コード'] == code]
436
+ if daigakuin_sub.shape[0] > 0:
437
+ total += int(daigakuin_sub['合計'].mean())
438
+
439
+ totals.append(total)
440
+
441
+ kougi_list['全体受講者数'] = totals
442
+ jukou = kougi_list[kougi_list['時限'].str.contains('後期')]
443
+
444
+
445
+ # 演習ゼロ調整
446
+ semi_zero = []
447
+ for teacher, i in kyouin.iterrows():
448
+
449
+ sub = zenki_moto_data[zenki_moto_data['教員名'] == teacher]
450
+ sub = sub[sub['分類'] == '大学院演習']
451
+ total = sub['受講者数'].sum()
452
+ flag = 0
453
+ if total == 0:
454
+ flag = -2
455
+ semi_zero.append([teacher, flag, i['講師'], flag * (1 - i['講師'] )])
456
+
457
+ semi_df = pd.DataFrame(semi_zero, columns=['教員名', '演習受講者ゼロ', '講師', '最終調整'])
458
+
459
+
460
+ zenki['2025年年度後期担当単位数(演習除く)'] = np.nan
461
+ teacher_list = zenki['教員名(敬称略)'].tolist()
462
+
463
+ for i in range(len(kabusoku)):
464
+ teacher = kabusoku.loc[i]['教員名(敬称略)']
465
+ if teacher in teacher_list:
466
+ idx = teacher_list.index(teacher)
467
+ zenki.loc[idx, '2025年年度後期担当単位数(演習除く)'] = kabusoku.loc[i]['2025年度担当単位数(演習除く)']
468
+ zenki.loc[idx, '備考1'] = f"前期: {zenki.loc[idx, '備考1']}\n後期: {kabusoku.loc[i]['備考1']}"
469
+ zenki.loc[idx, '備考2'] = kabusoku.loc[i]['備考2']
470
+
471
+ # change column name '2025年度前期における過不足' --> '2025年度における過不足'
472
+ zenki.rename(columns={'2025年度前期における過不足': '2025年度における過不足'}, inplace=True)
473
+ zenki.rename(columns={'2025年度担当単位数(演習除く)': '2025年年度前期担当単位数(演習除く)'}, inplace=True)
474
+ #zenki['2025年度における過不足'] += zenki['2025年年度後期担当単位数(演習除く)']
475
+
476
+ # insert the column '2025年年度後期担当単位数(演習除く)' after '2025年年度前期担当単位数(演習除く)'
477
+ col_idx = zenki.columns.get_loc('2025年年度前期担当単位数(演習除く)') + 1
478
+ zenki.insert(col_idx, '2025年年度後期担当単位数(演習除く)', zenki.pop('2025年年度後期担当単位数(演習除く)'))
479
+ zenki['演習ゼロ調整'] = semi_df['最終調整']
480
+
481
+ col_idx = zenki.columns.get_loc('受講者ゼロ調整')
482
+ zenki.insert(col_idx + 1, '演習ゼロ調整', zenki.pop('演習ゼロ調整'))
483
+
484
+
485
+ # 最終過不足計算
486
+ zenki['2025年度における過不足'] = zenki['2025年年度前期担当単位数(演習除く)'] + zenki['2025年年度後期担当単位数(演習除く)'] - zenki['年平均の標準負担単位数(演習除く)'] - zenki['受講者ゼロ調整'] + zenki['演習ゼロ調整'] + zenki['その他調整']
487
+
488
+ #return zenki,daigakuin, kougi_list, zenki_moto_data , kyouin
489
+
490
+ kabusoku.to_excel('kabusoku_kabusoku.xlsx', index=False)
491
+ zenki_moto_data.to_excel('kabusoku_moto.xlsx', index=False)
492
+
493
+ # 前期過不足
494
+ past_kahusoku = pd.read_excel(d5, sheet_name='2025年度前期における過不足(まとめ)')
495
+ past_moto_data = pd.read_excel(d5, sheet_name='前期元データ')
496
+
497
+ ruiseki = []
498
+ colnames = ['かな', '教員名(敬称略)', '2024年度累計過不足', '2025年度過不足', '2025年度累計過不足', '備考']
499
+ for i0, i in zenki.iterrows():
500
+ name = i['教員名(敬称略)']
501
+ kana = i['かな']
502
+ k = i['2025年度における過不足']
503
+ ruiseki.append([kana, name, 0, k, 0, ''])
504
+ ruiseki = pd.DataFrame(ruiseki, columns=colnames)
505
+ if d6 is not None:
506
+ past_ruiseki = pd.read_excel(d6, sheet_name='累計過不足')
507
+ #ruiseki['2024年度累計過不足'] = past_ruiseki.iloc[:,-2]
508
+ for i in range(len(ruiseki)):
509
+ name = ruiseki.loc[i]['かな']
510
+ sub = past_ruiseki[past_ruiseki['かな'] == name]
511
+ if sub.shape[0] > 0:
512
+ print(name, sub)
513
+ ruiseki.loc[i, '2024年度累計過不足'] = sub.iloc[0, -2]
514
+ ruiseki.loc[i, '2025年度累計過不足'] = ruiseki.loc[i]['2024年度累計過不足'] + ruiseki.loc[i]['2025年度過不足']
515
+
516
+ # NaNが入っている行を���す
517
+ nan_rows = ruiseki[ruiseki.isnull().any(axis=1)]
518
+ ruiseki.loc[nan_rows.index, '備考'] = '新任の先生なので2024年度の累積過不足は0。'
519
+ # fill NaN with 0
520
+ ruiseki.iloc[:,:-1] = ruiseki.iloc[:,:-1].fillna(0)
521
+
522
+ # kabusoku, zenki_moto_data as different sheet for 1 excel file
523
+ with pd.ExcelWriter('★2025年度過不足.xlsx') as writer:
524
+ zenki.to_excel(writer, sheet_name='2025年度における過不足(まとめ)', index=False)
525
+ ruiseki.to_excel(writer, sheet_name='累計過不足', index=False)
526
+ past_kahusoku.to_excel(writer, sheet_name='2025年度前期における過不足(まとめ)', index=False)
527
+ past_moto_data.to_excel(writer, sheet_name='2025年度前期元データ', index=False)
528
+ df_sorted.to_excel(writer, sheet_name='後期元データ', index=False)
529
+ ng_df.to_excel(writer, sheet_name='資料1 対象外科目', index=False)
530
+ zengaku_df.to_excel(writer, sheet_name='資料2 全学講義', index=False)
531
+ jukou.to_excel(writer, sheet_name='資料3 講義集計', index=False)
532
+ semi_df.to_excel(writer, sheet_name='資料4 演習ゼロ調整', index=False)
533
+
534
+
535
+ style_excel()
536
+ return ['★2025年度過不足.xlsx']
537
+
538
+ # Excelのスタイルを調整
539
+ # File :'★2025年度過不足.xlsx', sheet_name='2025年度における過不足(まとめ)'
540
+ import openpyxl
541
+ # 1. 列備考1を折り返し表示
542
+ def style_excel():
543
+ from openpyxl import load_workbook
544
+ from openpyxl.styles import Alignment
545
+
546
+ file = '★2025年度過不足.xlsx'
547
+ file2 = '★2025年度過不足.xlsx'
548
+ wb = load_workbook(file)
549
+ ws = wb['2025年度における過不足(まとめ)']
550
+
551
+ # すべてを折り返し表示
552
+ for row in ws.iter_rows():
553
+ for cell in row:
554
+ cell.alignment = Alignment(wrap_text=True, vertical='top')
555
+
556
+ # 備考1, 備考2の列幅を調整 (65)
557
+ ws.column_dimensions['A'].width = 10
558
+ ws.column_dimensions['B'].width = 10
559
+ ws.column_dimensions['K'].width = 65
560
+ ws.column_dimensions['L'].width = 65
561
+
562
+ # 1行目をセンター揃え
563
+ for cell in ws[1]:
564
+ cell.alignment = Alignment(horizontal='center', vertical='center', wrap_text=True)
565
+
566
+ # J列の2行目から、 253 233 217の背景色を設定、すべて赤の太字にする
567
+ for row in ws.iter_rows(min_row=2):
568
+ cell = row[9] # J列
569
+ cell.font = cell.font.copy(bold=True, color='FF0000')
570
+ cell.fill = openpyxl.styles.PatternFill(start_color='FDE9D9', end_color='FDE9D9', fill_type='solid')
571
+ #すべて格子線を引く
572
+ from openpyxl.styles import Border, Side
573
+ thin_border = Border(left=Side(style='thin'),
574
+ right=Side(style='thin'),
575
+ top=Side(style='thin'),
576
+ bottom=Side(style='thin'))
577
+ for row in ws.iter_rows():
578
+ for cell in row:
579
+ cell.border = thin_border
580
+
581
+ # 累計過不足シート
582
+ ws = wb['累計過不足']
583
+
584
+ # 名前
585
+ ws.column_dimensions['A'].width = 20
586
+ ws.column_dimensions['B'].width = 20
587
+
588
+ # 1行目だけ、折り返し、センター揃え
589
+ for cell in ws[1]:
590
+ cell.alignment = Alignment(horizontal='center', vertical='center', wrap_text=True)
591
+
592
+ # 最後の列のwidth = 65
593
+ last_col = ws.max_column
594
+ last_col_letter = openpyxl.utils.get_column_letter(last_col)
595
+ ws.column_dimensions[last_col_letter].width = 65
596
+ # すべて格子線を引く
597
+ for row in ws.iter_rows():
598
+ for cell in row:
599
+ cell.border = thin_border
600
+
601
+ # E列、2行目から、228 223 236 の背景、赤い太字
602
+ for row in ws.iter_rows(min_row=2):
603
+ cell = row[4]
604
+ cell.font = cell.font.copy(bold=True, color='FF0000')
605
+ cell.fill = openpyxl.styles.PatternFill(start_color='E4DFEC', end_color='E4DFEC', fill_type='solid')
606
+
607
+
608
+ wb.save(file2)
609
+
610
+
611
+
612
+
613
+ import gradio as gr
614
+ import os
615
+ import shutil
616
+ from datetime import datetime
617
+
618
+ # 仮の計算関数(あなたの処理ロジックに置き換えてください)
619
+ def process_files(timetable_file, burden_file, undergrad_file, grad_file):
620
+ output_dir = "output_files"
621
+ os.makedirs(output_dir, exist_ok=True)
622
+
623
+ # 仮の出力ファイルを作成(本来はここで実際の処理を行う)
624
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
625
+ output_paths = []
626
+ for i in range(1, 4):
627
+ filename = f"出力ファイル_{i}_{timestamp}.xlsx"
628
+ path = os.path.join(output_dir, filename)
629
+ with open(path, "w") as f:
630
+ f.write("これは出力ファイルのサンプルです。\n")
631
+ output_paths.append(path)
632
+
633
+ return output_paths
634
+
635
+
636
+ def main():
637
+ with gr.Blocks(title="授業コード作成ツール") as demo:
638
+ with gr.Tabs():
639
+ with gr.TabItem("1. 授業コードの作成", id="code_generation"):
640
+ gr.Markdown("## 📘 授業コードの自動作成")
641
+ gr.Markdown("以下の4つのファイルをアップロードしてください。処理が完了すると、出力ファイルをダウンロードできます。")
642
+
643
+ with gr.Row():
644
+ timetable_file = gr.File(label="(学部・大学院)2025年度時間割", file_types=[".xlsx"])
645
+ burden_file = gr.File(label="授業負担自動計算ファイル", file_types=[".xlsx"])
646
+
647
+ with gr.Row():
648
+ undergrad_file = gr.File(label="(学部)授業別受講者人数表", file_types=[".xlsx"])
649
+ grad_file = gr.File(label="(大学院)授業別受講者人数表", file_types=[".xlsx"])
650
+
651
+ with gr.Row():
652
+ zenki_file = gr.File(label="(前期)2025年度過不足ファイル", file_types=[".xlsx"])
653
+ kyonen_file = gr.File(label="2024年度過不足ファイル", file_types=[".xlsx"], optional=True)
654
+
655
+ submit_btn = gr.Button("📊 授業コードを作成")
656
+ output_files = gr.File(label="出力ファイル", file_types=[".xlsx"], interactive=False, file_count="multiple")
657
+
658
+ submit_btn.click(
659
+ fn=main_func,
660
+ inputs=[timetable_file, burden_file, undergrad_file, grad_file, zenki_file, kyonen_file],
661
+ outputs=output_files
662
+ )
663
+
664
+ if __name__ == "__main__":
665
+ demo.launch()