aslasacacc commited on
Commit
f19a974
·
1 Parent(s): 4253e50

pesan update, misal: memperbaiki bug di halaman laporan

Browse files
Files changed (3) hide show
  1. .gitignore +7 -15
  2. revisi_skripsi +1 -0
  3. tempCodeRunnerFile.py +253 -0
.gitignore CHANGED
@@ -1,23 +1,15 @@
1
- # Folder Virtual Environment Python (SANGAT PENTING!)
2
- # Isinya bisa ratusan MB dan spesifik untuk komputermu saja.
3
  .venv/
 
 
4
 
5
- # Folder cache Python (Tidak berguna di server)
6
  __pycache__/
7
  *.pyc
8
 
9
- # File Environment berisi data rahasia (SANGAT PENTING!)
10
- # JANGAN PERNAH upload file ini. Isinya bisa password database, API key, dll.
11
  .env
12
-
13
- # Pengaturan spesifik VS Code (Tidak dibutuhkan di server)
14
  .vscode/
15
 
16
- # File sementara dari extension Code Runner
17
- tempCodeRunnerFile.py
18
-
19
- # Folder file sementara (jika ada)
20
- temp_files/
21
-
22
- # Submodul/folder sisa dari error sebelumnya
23
- revisi_skripsi/
 
1
+ # Folder Virtual Environment
 
2
  .venv/
3
+ venv/
4
+ env/
5
 
6
+ # Python cache
7
  __pycache__/
8
  *.pyc
9
 
10
+ # File konfigurasi lokal (sering berisi data rahasia)
 
11
  .env
 
 
12
  .vscode/
13
 
14
+ # File sistem operasi
15
+ .DS_Store
 
 
 
 
 
 
revisi_skripsi ADDED
@@ -0,0 +1 @@
 
 
1
+ Subproject commit 4f9a9e31d91ffe69d300621d9852937447d3e59d
tempCodeRunnerFile.py ADDED
@@ -0,0 +1,253 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py (Versi Revisi Siap Deploy)
2
+
3
+ import dash
4
+ import dash_bootstrap_components as dbc
5
+ from dash import Dash, dcc, html, Output, Input, State, no_update
6
+
7
+ # Impor dari proyek Anda
8
+ from pages import (
9
+ beranda,
10
+ analisis_tren_penyakit,
11
+ distribusi_kasus_demografi,
12
+ input_data,
13
+ laporan,
14
+ pengaturan
15
+ )
16
+ from auth import login, signup
17
+ from components import sidebar
18
+
19
+ # -----------------------------------------------------------------------------
20
+ # BAGIAN 1: INISIALISASI APLIKASI (PERUBAHAN UTAMA DI SINI)
21
+ # -----------------------------------------------------------------------------
22
+ # Tidak perlu mengimpor Flask atau menginisialisasi server Flask secara manual lagi.
23
+ # Dash akan melakukannya secara otomatis.
24
+
25
+ # Konfigurasi Tema
26
+ THEME_LIGHT = dbc.themes.FLATLY
27
+ THEME_DARK = dbc.themes.DARKLY
28
+
29
+ # Inisialisasi aplikasi Dash
30
+ app = Dash(
31
+ __name__,
32
+ suppress_callback_exceptions=True,
33
+ external_stylesheets=[THEME_LIGHT, '/assets/style.css'],
34
+ meta_tags=[{"name": "viewport", "content": "width=device-width, initial-scale=1"}]
35
+ )
36
+
37
+ # Baris ini SANGAT PENTING untuk deployment.
38
+ # Gunicorn akan mencari variabel bernama 'server'.
39
+ server = app.server
40
+
41
+
42
+ # -----------------------------------------------------------------------------
43
+ # BAGIAN 2: LAYOUT UTAMA APLIKASI (TIDAK ADA PERUBAHAN)
44
+ # -----------------------------------------------------------------------------
45
+ app.layout = html.Div([
46
+ dcc.Location(id='url', refresh=False),
47
+ dcc.Store(id='login-status', storage_type='session'),
48
+ dcc.Store(id='user-theme-preference-store', storage_type='local'),
49
+ dcc.Store(id='previous-url-store', storage_type='session'),
50
+ html.Div(id='app-wrapper'),
51
+ dbc.Modal(
52
+ [
53
+ dbc.ModalHeader(dbc.ModalTitle("Konfirmasi Logout")),
54
+ dbc.ModalBody("Apakah Anda yakin ingin keluar dari sesi ini?"),
55
+ dbc.ModalFooter([
56
+ dbc.Button("Tidak", id="logout-confirm-no", color="secondary", className="ms-auto", n_clicks=0),
57
+ dbc.Button("Ya, Logout", id="logout-confirm-yes", color="danger", className="ms-2", n_clicks=0),
58
+ ]),
59
+ ],
60
+ id="logout-confirm-modal",
61
+ is_open=False,
62
+ centered=True,
63
+ ),
64
+ dcc.Location(id='logout-redirect-location', refresh=True)
65
+ ])
66
+
67
+ # -----------------------------------------------------------------------------
68
+ # BAGIAN 3: CALLBACKS (TIDAK ADA PERUBAHAN)
69
+ # -----------------------------------------------------------------------------
70
+
71
+ # --- Callbacks untuk Tema (JavaScript Inline) ---
72
+ app.clientside_callback(
73
+ """
74
+ function(pathname, storedThemePreference) {
75
+ let themeToApply = 'LIGHT';
76
+ if (storedThemePreference && typeof storedThemePreference.theme === 'string') {
77
+ themeToApply = storedThemePreference.theme;
78
+ }
79
+ const lightThemeUrl = "https://cdn.jsdelivr.net/npm/[email protected]/dist/flatly/bootstrap.min.css";
80
+ const darkThemeUrl = "https://cdn.jsdelivr.net/npm/[email protected]/dist/darkly/bootstrap.min.css";
81
+ let newThemeUrl = lightThemeUrl;
82
+ document.body.classList.remove('theme-dark', 'theme-light');
83
+ if (themeToApply === 'DARK') {
84
+ newThemeUrl = darkThemeUrl;
85
+ document.body.classList.add('theme-dark');
86
+ } else {
87
+ newThemeUrl = lightThemeUrl;
88
+ document.body.classList.add('theme-light');
89
+ }
90
+ let themeLink = document.getElementById('bootstrap-theme');
91
+ if (!themeLink) {
92
+ themeLink = document.createElement('link');
93
+ themeLink.id = 'bootstrap-theme';
94
+ themeLink.rel = 'stylesheet';
95
+ themeLink.type = 'text/css';
96
+ document.getElementsByTagName('head')[0].appendChild(themeLink);
97
+ }
98
+ if (themeLink.href !== newThemeUrl) {
99
+ themeLink.href = newThemeUrl;
100
+ }
101
+ return null;
102
+ }
103
+ """,
104
+ Output('app-wrapper', 'className'),
105
+ Input('url', 'pathname'),
106
+ State('user-theme-preference-store', 'data')
107
+ )
108
+ app.clientside_callback(
109
+ """
110
+ function(themePreferenceFromStore) {
111
+ let themeToApply = 'LIGHT';
112
+ if (themePreferenceFromStore && typeof themePreferenceFromStore.theme === 'string') {
113
+ themeToApply = themePreferenceFromStore.theme;
114
+ }
115
+ const lightThemeUrl = "https://cdn.jsdelivr.net/npm/[email protected]/dist/flatly/bootstrap.min.css";
116
+ const darkThemeUrl = "https://cdn.jsdelivr.net/npm/[email protected]/dist/darkly/bootstrap.min.css";
117
+ let newThemeUrl = lightThemeUrl;
118
+ document.body.classList.remove('theme-dark', 'theme-light');
119
+ if (themeToApply === 'DARK') {
120
+ newThemeUrl = darkThemeUrl;
121
+ document.body.classList.add('theme-dark');
122
+ } else {
123
+ newThemeUrl = lightThemeUrl;
124
+ document.body.classList.add('theme-light');
125
+ }
126
+ let themeLink = document.getElementById('bootstrap-theme');
127
+ if (!themeLink) {
128
+ themeLink = document.createElement('link');
129
+ themeLink.id = 'bootstrap-theme';
130
+ themeLink.rel = 'stylesheet';
131
+ themeLink.type = 'text/css';
132
+ document.getElementsByTagName('head')[0].appendChild(themeLink);
133
+ }
134
+ if (themeLink.href !== newThemeUrl) {
135
+ themeLink.href = newThemeUrl;
136
+ }
137
+ return null;
138
+ }
139
+ """,
140
+ Output('app-wrapper', 'className', allow_duplicate=True),
141
+ Input('user-theme-preference-store', 'data'),
142
+ prevent_initial_call=True
143
+ )
144
+
145
+ # Callback untuk Navigasi Halaman, Kontrol Sidebar, dan Modal Logout
146
+ @app.callback(
147
+ Output('app-wrapper', 'children'),
148
+ Output('logout-confirm-modal', 'is_open'),
149
+ Input('url', 'pathname'),
150
+ State('login-status', 'data')
151
+ )
152
+ def display_page_logic(pathname, login_data):
153
+ is_logged_in = login_data and login_data.get('logged_in', False)
154
+ no_sidebar_pages = ['/login', '/signup']
155
+ open_logout_modal = False
156
+
157
+ if pathname == '/logout' and is_logged_in:
158
+ open_logout_modal = True
159
+
160
+ # KASUS 1: Pengguna belum login
161
+ if not is_logged_in:
162
+ if pathname in no_sidebar_pages or pathname == '/' or pathname is None:
163
+ if pathname == '/signup':
164
+ return signup.layout, False
165
+ else:
166
+ return login.layout, False
167
+ else:
168
+ return dcc.Location(pathname="/login", id="redirect-to-login-unauth"), False
169
+
170
+ # KASUS 2: Pengguna sudah login
171
+ else:
172
+ if pathname == '/logout':
173
+ page_layout_content = beranda.layout
174
+ elif pathname in no_sidebar_pages or pathname == '/' or pathname is None:
175
+ return dcc.Location(pathname="/beranda", id="redirect-to-home-auth"), False
176
+ elif pathname == '/beranda':
177
+ page_layout_content = beranda.layout
178
+ elif pathname == '/analisis_tren_penyakit':
179
+ page_layout_content = analisis_tren_penyakit.layout
180
+ elif pathname == '/distribusi_kasus_demografi':
181
+ page_layout_content = distribusi_kasus_demografi.layout
182
+ elif pathname == '/input_data':
183
+ page_layout_content = input_data.layout
184
+ elif pathname == '/laporan':
185
+ page_layout_content = laporan.layout
186
+ elif pathname == '/pengaturan':
187
+ page_layout_content = pengaturan.layout
188
+ else:
189
+ page_layout_content = html.H1("404 - Halaman Tidak Ditemukan", className="text-center mt-5")
190
+
191
+ return html.Div([
192
+ sidebar.sidebar_layout,
193
+ html.Div(page_layout_content, id="page-content")
194
+ ]), open_logout_modal
195
+
196
+ # Callback untuk menyimpan URL sebelum logout
197
+ @app.callback(
198
+ Output('previous-url-store', 'data'),
199
+ Input('url', 'pathname'),
200
+ State('previous-url-store', 'data')
201
+ )
202
+ def store_previous_url(current_pathname, last_stored_url):
203
+ excluded_paths = ['/logout', '/login', '/signup']
204
+ if current_pathname not in excluded_paths and current_pathname != last_stored_url:
205
+ return current_pathname
206
+ return no_update
207
+
208
+ # Callback untuk Update Profil di Sidebar
209
+ @app.callback(
210
+ Output('sidebar-profile-name', 'children'),
211
+ Output('sidebar-profile-section', 'style'),
212
+ Input('login-status', 'data'),
213
+ Input('url', 'pathname')
214
+ )
215
+ def update_sidebar_profile(login_data, pathname):
216
+ no_sidebar_pages_or_logout = ['/login', '/signup', '/logout']
217
+ if login_data and login_data.get('logged_in'):
218
+ nama_pengguna = login_data.get('nama_lengkap', login_data.get('username', 'Pengguna'))
219
+ if pathname not in no_sidebar_pages_or_logout:
220
+ return nama_pengguna, {'display': 'block'}
221
+ else:
222
+ return no_update, {'display': 'none'}
223
+ return "Nama Pengguna", {'display': 'none'}
224
+
225
+ # Callback untuk Aksi Modal Logout
226
+ @app.callback(
227
+ Output('logout-redirect-location', 'pathname'),
228
+ Output('login-status', 'clear_data', allow_duplicate=True),
229
+ Output('logout-confirm-modal', 'is_open', allow_duplicate=True),
230
+ Input('logout-confirm-yes', 'n_clicks'),
231
+ Input('logout-confirm-no', 'n_clicks'),
232
+ State('previous-url-store', 'data'),
233
+ prevent_initial_call=True
234
+ )
235
+ def handle_logout_confirmation_app(n_yes, n_no, previous_url):
236
+ triggered_id = dash.callback_context.triggered_id if dash.callback_context.triggered_id else None
237
+
238
+ if triggered_id == 'logout-confirm-yes':
239
+ return '/login', True, False
240
+
241
+ elif triggered_id == 'logout-confirm-no':
242
+ url_to_return = previous_url if previous_url else '/beranda'
243
+ return url_to_return, False, False
244
+
245
+ return no_update, no_update, False
246
+
247
+ # -----------------------------------------------------------------------------
248
+ # BAGIAN 4: MENJALANKAN APLIKASI (TIDAK ADA PERUBAHAN)
249
+ # -----------------------------------------------------------------------------
250
+ # Blok ini memastikan server development hanya berjalan saat file ini dieksekusi langsung,
251
+ # dan tidak akan berjalan saat diimpor oleh Gunicorn di server produksi.
252
+ if __name__ == '__main__':
253
+ app.run(debug=True, port=8050)