File size: 7,886 Bytes
48b5cb1
 
 
 
6a0a429
 
 
 
 
 
 
 
48b5cb1
 
 
 
 
 
6a0a429
 
 
 
 
48b5cb1
 
6a0a429
 
 
a0d554d
6a0a429
 
cf29b06
6a0a429
 
 
 
 
 
 
cf29b06
6a0a429
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dbac8dd
48b5cb1
cf29b06
6a0a429
 
 
cf29b06
6a0a429
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cf29b06
6a0a429
 
 
 
a0d554d
cf29b06
6a0a429
 
 
 
 
 
 
 
 
 
 
cf29b06
6a0a429
 
 
 
 
 
cf29b06
6a0a429
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cf29b06
 
6a0a429
 
 
 
 
 
 
 
 
 
cf29b06
48b5cb1
6a0a429
 
 
 
 
 
 
48b5cb1
 
 
6a0a429
 
48b5cb1
 
 
 
6a0a429
48b5cb1
 
 
0279df1
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
import logging

import gradio as gr

from backend import get_backend
from ui_components import (
    build_data_info,
    build_multi_year_summary,
    build_prediction_summary,
    get_forecast_placeholder,
    get_prediction_placeholder,
)
from utils import setup_logging

setup_logging("INFO")
logger = logging.getLogger("GradioApp")


# Backend Interface
def get_data_info() -> str:
    backend = get_backend()
    data = backend.get_data_info()
    return build_data_info(data)


def generate_predictions(year: int, semester: int):
    backend = get_backend()
    result = backend.generate_predictions(year, semester)

    if result.error:
        return f"Error: {result.error}", None, None

    summary_html = build_prediction_summary(result.summary_data)
    return summary_html, result.predictions_df, result.comparison_df


def generate_multi_year_forecast(year: int, semester: int, years_ahead: int = 3):
    backend = get_backend()
    result = backend.generate_multi_year_forecast(year, semester, years_ahead)

    if result.error:
        return f"Error: {result.error}", None

    summary_html = build_multi_year_summary(result.summary_data)
    return summary_html, result.forecast_df


def update_ui_with_predictions(year: int, semester: int):
    summary, all_predictions, comparison = generate_predictions(year, semester)

    logger.info(
        f"UI Update: comparison is None: {comparison is None}, "
        f"empty: {comparison.empty if comparison is not None else 'N/A'}"
    )

    if comparison is not None and not comparison.empty:
        logger.info(f"Showing comparison table with {len(comparison)} rows")
        return (
            summary,
            all_predictions,
            gr.update(open=True),
            gr.update(
                value=f"Validasi terhadap {len(comparison)} mata kuliah - "
                "termasuk perbandingan jumlah kelas aktual vs prediksi"
            ),
            gr.update(value=comparison),
        )
    else:
        logger.info("Hiding comparison table - no data available")
        return (
            summary,
            all_predictions,
            gr.update(open=False),
            gr.update(value="Tidak ada data validasi untuk prediksi masa depan"),
            gr.update(value=None),
        )


# Gradio UI
def create_gradio_app() -> gr.Blocks:
    """Create and configure the Gradio application."""

    with gr.Blocks(title="SKS Enrollment Predictor") as demo:
        # Header
        gr.Markdown("# Course Enrollment & Class Capacity Predictor")
        gr.Markdown(
            "Sistem prediksi **jumlah kelas yang perlu dibuka** berdasarkan "
            "forecasting enrollment dengan mempertimbangkan kapasitas maksimum per kelas."
        )

        with gr.Tabs():
            # Single Year
            with gr.TabItem("Prediksi Semester"):
                with gr.Row():
                    with gr.Column(scale=1, min_width=300):
                        year_input = gr.Number(
                            label="Tahun",
                            value=2025,
                            precision=0,
                            minimum=2020,
                            maximum=2030,
                        )

                        semester_input = gr.Radio(
                            choices=[("1 (Ganjil)", 1), ("2 (Genap)", 2)],
                            label="Semester",
                            value=2,
                        )

                        predict_btn = gr.Button(
                            "Generate Predictions",
                            variant="primary",
                            size="lg",
                        )

                        gr.Markdown("---")

                        with gr.Accordion("Dataset Info", open=False):
                            data_info_output = gr.HTML()
                            demo.load(
                                fn=get_data_info, inputs=[], outputs=data_info_output
                            )

                    with gr.Column(scale=3):
                        summary_output = gr.HTML(value=get_prediction_placeholder())

                gr.Markdown("---")

                gr.Markdown("### Rekomendasi Jumlah Kelas per Mata Kuliah")
                gr.Markdown(
                    "*Jumlah kelas dihitung berdasarkan prediksi enrollment ÷ "
                    "kapasitas per kelas*"
                )
                all_predictions_output = gr.Dataframe(
                    label="",
                    wrap=True,
                    interactive=False,
                )

                with gr.Accordion(
                    "Detail Validasi", open=False
                ) as comparison_accordion:
                    comparison_info = gr.Markdown(
                        value="Data validasi muncul ketika data aktual tersedia"
                    )
                    comparison_output = gr.Dataframe(
                        label="",
                        wrap=True,
                        interactive=False,
                    )

            # Multi-Year Forecast
            with gr.TabItem("Proyeksi Multi-Tahun"):
                gr.Markdown("### Forecasting Kebutuhan Kelas Beberapa Tahun ke Depan")
                gr.Markdown(
                    "Memprediksi tren jumlah mahasiswa dan kebutuhan kelas "
                    "untuk perencanaan jangka panjang."
                )

                with gr.Row():
                    with gr.Column(scale=1):
                        forecast_year = gr.Number(
                            label="Tahun Mulai",
                            value=2025,
                            precision=0,
                            minimum=2020,
                            maximum=2030,
                        )

                        forecast_semester = gr.Radio(
                            choices=[("1 (Ganjil)", 1), ("2 (Genap)", 2)],
                            label="Semester",
                            value=2,
                        )

                        forecast_years = gr.Slider(
                            label="Tahun ke Depan",
                            minimum=1,
                            maximum=5,
                            value=3,
                            step=1,
                        )

                        forecast_btn = gr.Button(
                            "Generate Forecast",
                            variant="primary",
                            size="lg",
                        )

                    with gr.Column(scale=3):
                        forecast_summary = gr.HTML(value=get_forecast_placeholder())

                gr.Markdown("---")
                gr.Markdown("### Detail Proyeksi per Mata Kuliah per Tahun")
                forecast_table = gr.Dataframe(
                    label="",
                    wrap=True,
                    interactive=False,
                )

        predict_btn.click(
            fn=update_ui_with_predictions,
            inputs=[year_input, semester_input],
            outputs=[
                summary_output,
                all_predictions_output,
                comparison_accordion,
                comparison_info,
                comparison_output,
            ],
        )

        forecast_btn.click(
            fn=generate_multi_year_forecast,
            inputs=[forecast_year, forecast_semester, forecast_years],
            outputs=[forecast_summary, forecast_table],
        )

    return demo


logger.info("Starting Gradio app...")
backend = get_backend()
init_success = backend.initialize()

if not init_success:
    logger.error("Failed to initialize system. App may not work correctly.")

demo = create_gradio_app()

# Launch the app
if __name__ == "__main__":
    demo.launch(
        server_name="0.0.0.0",
        server_port=7860,
        share=False,
        show_error=True,
    )