Varriety commited on
Commit
e2055c0
Β·
verified Β·
1 Parent(s): b7ef13e

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +128 -177
src/streamlit_app.py CHANGED
@@ -23,12 +23,11 @@ DetectorFactory.seed = 0
23
  # SETTING PATH ABSOLUT UNTUK GAMBAR
24
  # ==============================
25
  BASE_DIR = os.path.dirname(os.path.abspath(__file__))
26
-
27
  img_hero = os.path.join(BASE_DIR, "crypto-currency-concept-830px.png")
28
  img_batch = os.path.join(BASE_DIR, "slice3-1-1536x830.png")
29
 
30
  # ==============================
31
- # KONFIGURASI HALAMAN & STATE NAVIGASI
32
  # ==============================
33
  st.set_page_config(page_title="SKRIPSI - Sentimen BTC", page_icon="β‚Ώ", layout="wide", initial_sidebar_state="collapsed")
34
 
@@ -36,194 +35,147 @@ if 'page' not in st.session_state:
36
  st.session_state.page = "uji_kalimat"
37
 
38
  # ==============================
39
- # GLOBAL CSS (MINIMALIST & PROFESSIONAL)
40
  # ==============================
41
  st.markdown("""
42
  <style>
43
- @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
 
44
 
45
- /* Menyembunyikan elemen default Streamlit agar terlihat seperti website asli */
46
  #MainMenu {visibility: hidden;}
47
  footer {visibility: hidden;}
48
  header {visibility: hidden;}
49
 
50
- /* Mengatur padding utama halaman */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  .block-container {
52
  padding-top: 2rem !important;
53
- padding-bottom: 2rem !important;
54
  max-width: 1200px !important;
55
  }
56
 
57
- /* Global Font */
58
- html, body, [class*="css"] {
59
- font-family: 'Inter', sans-serif !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  }
61
 
62
- /* Styling Navbar Container */
63
- .nav-container {
64
- display: flex;
65
- justify-content: space-between;
66
- align-items: center;
67
- padding-bottom: 1.5rem;
68
- margin-bottom: 2rem;
69
- border-bottom: 1px solid rgba(128, 128, 128, 0.2);
 
 
 
70
  }
71
-
72
- /* Styling Tombol Standar Streamlit menjadi Minimalis */
73
- div[data-testid="stButton"] > button {
74
- border-radius: 30px;
75
- font-weight: 600;
76
- padding: 0.5rem 1.5rem;
77
- transition: all 0.3s ease;
78
- border: 1px solid transparent;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  }
80
 
81
- /* Menghilangkan border focus merah default Streamlit */
82
- div[data-testid="stButton"] > button:focus:not(:active) {
83
- border-color: transparent;
84
- box-shadow: none;
85
- color: inherit;
 
 
 
86
  }
87
  </style>
88
  """, unsafe_allow_html=True)
89
 
90
  # ==============================
91
- # TEMA DINAMIS PER HALAMAN
92
- # ==============================
93
- if st.session_state.page == "uji_kalimat":
94
- # TEMA DARK/ORANGE (Premium Landing Page Look)
95
- st.markdown("""
96
- <style>
97
- .stApp {
98
- background-color: #0F172A; /* Dark Slate Minimalist */
99
- color: #F8FAFC;
100
- }
101
- h1, h2, h3, h4, h5, h6, p, label {
102
- color: #F8FAFC !important;
103
- }
104
-
105
- /* Tombol Aksi Utama - Orange Bitcoin */
106
- div[data-testid="stButton"] > button {
107
- background-color: #F7931A !important;
108
- color: #FFFFFF !important;
109
- border: none;
110
- box-shadow: 0 4px 6px rgba(247, 147, 26, 0.2);
111
- }
112
- div[data-testid="stButton"] > button:hover {
113
- background-color: #E68310 !important;
114
- transform: translateY(-2px);
115
- }
116
-
117
- /* Navigasi Button di Halaman 1 */
118
- .nav-btn-active > button {
119
- background-color: transparent !important;
120
- border: 2px solid #F7931A !important;
121
- color: #F7931A !important;
122
- }
123
- .nav-btn-inactive > button {
124
- background-color: transparent !important;
125
- border: 1px solid #475569 !important;
126
- color: #94A3B8 !important;
127
- }
128
-
129
- /* Styling Text Area Input */
130
- .stTextArea textarea {
131
- background-color: #1E293B !important;
132
- color: #F8FAFC !important;
133
- border: 1px solid #334155 !important;
134
- border-radius: 12px;
135
- font-size: 1rem;
136
- padding: 1rem;
137
- }
138
- .stTextArea textarea:focus {
139
- border-color: #F7931A !important;
140
- box-shadow: 0 0 0 1px #F7931A !important;
141
- }
142
- </style>
143
- """, unsafe_allow_html=True)
144
- else:
145
- # TEMA TERANG (Dashboard Analisis Clean)
146
- st.markdown("""
147
- <style>
148
- .stApp {
149
- background-color: #FFFFFF;
150
- color: #0F172A;
151
- }
152
- h1, h2, h3, h4, h5, h6, label {
153
- color: #0F172A !important;
154
- }
155
- p, span {
156
- color: #475569 !important;
157
- }
158
-
159
- /* Tombol Aksi Utama - Dark Navy */
160
- div[data-testid="stButton"] > button {
161
- background-color: #0F172A !important;
162
- color: #FFFFFF !important;
163
- border: none;
164
- }
165
- div[data-testid="stButton"] > button:hover {
166
- background-color: #1E293B !important;
167
- transform: translateY(-2px);
168
- }
169
-
170
- /* Navigasi Button di Halaman 2 */
171
- .nav-btn-inactive > button {
172
- background-color: transparent !important;
173
- border: 1px solid #CBD5E1 !important;
174
- color: #64748B !important;
175
- }
176
- .nav-btn-active > button {
177
- background-color: transparent !important;
178
- border: 2px solid #0F172A !important;
179
- color: #0F172A !important;
180
- }
181
-
182
- /* Styling Dataframe / Table */
183
- div[data-testid="stDataFrame"] {
184
- border: 1px solid #E2E8F0;
185
- border-radius: 12px;
186
- overflow: hidden;
187
- }
188
- </style>
189
- """, unsafe_allow_html=True)
190
-
191
- # ==============================
192
- # HEADER & NAVIGASI
193
  # ==============================
194
  def set_page(page_name):
195
  st.session_state.page = page_name
196
 
197
- # Menggunakan Columns untuk Navbar yang rapi
198
- col_logo, col_space, col_nav1, col_nav2 = st.columns([3, 4, 1.5, 1.5])
199
 
200
  with col_logo:
201
- # Logo text
202
- color_logo = "#F8FAFC" if st.session_state.page == "uji_kalimat" else "#0F172A"
203
- st.markdown(f"<h3 style='margin:0; padding:10px 0; font-weight:700; color:{color_logo} !important;'>β‚Ώitcoin Sentimen</h3>", unsafe_allow_html=True)
204
 
205
  with col_nav1:
206
- btn_class = "nav-btn-active" if st.session_state.page == "uji_kalimat" else "nav-btn-inactive"
207
- st.markdown(f'<div class="{btn_class}">', unsafe_allow_html=True)
208
- if st.button("Uji Kalimat πŸ“", use_container_width=True, key="nav_uji"):
209
  set_page("uji_kalimat")
210
  st.rerun()
211
- st.markdown('</div>', unsafe_allow_html=True)
212
 
213
  with col_nav2:
214
- btn_class = "nav-btn-active" if st.session_state.page == "analisis_batch" else "nav-btn-inactive"
215
- st.markdown(f'<div class="{btn_class}">', unsafe_allow_html=True)
216
- if st.button("Analisis Batch πŸ“Š", use_container_width=True, key="nav_batch"):
217
  set_page("analisis_batch")
218
  st.rerun()
219
- st.markdown('</div>', unsafe_allow_html=True)
220
 
221
- # Divider Navbar
222
- st.markdown("<div style='border-bottom: 1px solid rgba(128,128,128,0.2); margin-top: 10px; margin-bottom: 40px;'></div>", unsafe_allow_html=True)
223
 
224
 
225
  # ==============================
226
- # DOWNLOAD RESOURCES & LOAD MODELS (Tetap Sama)
227
  # ==============================
228
  @st.cache_resource
229
  def download_nltk_resources():
@@ -243,11 +195,11 @@ def load_all_models():
243
  roberta_large = pipeline("sentiment-analysis", model="siebert/sentiment-roberta-large-english", device=-1, truncation=True, max_length=512)
244
  return vader, bertweet, roberta, roberta_large
245
 
246
- with st.spinner('Mempersiapkan model NLP...'):
247
  vader, bertweet, roberta, roberta_large = load_all_models()
248
 
249
  # ==============================
250
- # FUNGSI CLEAN TEXT & MAPPING (Tetap Sama)
251
  # ==============================
252
  def clean_text(text):
253
  text = str(text).lower()
@@ -277,29 +229,29 @@ def get_daily_label(score):
277
 
278
 
279
  # ==============================================================================
280
- # HALAMAN 1: UJI KALIMAT (TEMA DARK/ORANGE)
281
  # ==============================================================================
282
  if st.session_state.page == "uji_kalimat":
283
  col_text, col_img = st.columns([1.1, 1], gap="large")
284
 
285
  with col_text:
286
  st.markdown("""
287
- <div style="padding-top: 2rem;">
288
- <h1 style="font-size: 3.5rem; line-height: 1.1; margin-bottom: 1.5rem; font-weight: 700; letter-spacing: -1px;">
289
  Bitcoin Volatility <br><span style="color: #F7931A;">vs Public Sentiment</span>
290
  </h1>
291
- <p style='font-size: 1.15rem; font-weight: 400; color: #94A3B8 !important; margin-bottom: 2rem;'>
292
  Analisis Volatilitas Harga Bitcoin Terhadap Sentimen Publik Pada Platform X Berbasis Python.
293
  </p>
294
- <div style="background-color: rgba(247, 147, 26, 0.1); border-left: 4px solid #F7931A; padding: 15px; border-radius: 4px; margin-bottom: 2rem;">
295
- <p style="margin: 0; font-size: 0.95rem; color: #E2E8F0 !important;">
296
- <b>Peneliti:</b> Arya Galuh Saputra (H1D022022)
297
  </p>
298
  </div>
299
  </div>
300
  """, unsafe_allow_html=True)
301
 
302
- user_input = st.text_area("Masukkan Tweet (Bahasa Inggris):", "Great, Bitcoin just crashed another 10% today.", height=120)
303
 
304
  st.markdown("<br>", unsafe_allow_html=True)
305
  analyze_btn = st.button("πŸš€ Analisis Sentimen Sekarang", use_container_width=True)
@@ -308,17 +260,16 @@ if st.session_state.page == "uji_kalimat":
308
  try:
309
  st.image(img_hero, use_container_width=True)
310
  except Exception:
311
- st.info(f"Visualisasi Hero akan muncul di sini. (Pastikan file {os.path.basename(img_hero)} tersedia)")
312
 
313
  if analyze_btn:
314
- st.markdown("<br><hr style='border-color: #334155;'><br>", unsafe_allow_html=True)
315
- st.markdown("<h3 style='text-align: center; margin-bottom: 2rem;'>πŸ“‹ Hasil Deteksi Sentimen</h3>", unsafe_allow_html=True)
316
 
317
  try:
318
  if detect(user_input) != 'en':
319
  st.warning("⚠️ Teks sepertinya bukan bahasa Inggris. Hasil prediksi mungkin memiliki bias.")
320
- except:
321
- pass
322
 
323
  text = clean_text(user_input)
324
 
@@ -354,14 +305,14 @@ if st.session_state.page == "uji_kalimat":
354
 
355
 
356
  # ==============================================================================
357
- # HALAMAN 2: ANALISIS BATCH DATA (TEMA TERANG/KREM CLEAN)
358
  # ==============================================================================
359
  elif st.session_state.page == "analisis_batch":
360
- # Tema Matplotlib
361
  plt.style.use('default')
362
  sns.set_theme(style="whitegrid", rc={
363
- "axes.facecolor": "#F8FAFC",
364
- "figure.facecolor": "#FFFFFF",
365
  "axes.edgecolor": "#E2E8F0",
366
  "text.color": "#0F172A",
367
  "xtick.color": "#64748B",
@@ -374,14 +325,14 @@ elif st.session_state.page == "analisis_batch":
374
  with col_upload:
375
  st.markdown("""
376
  <div style="padding-top: 1rem;">
377
- <h2 style="font-size: 2.5rem; margin-bottom: 0.5rem; font-weight: 700; letter-spacing: -0.5px;">Analisis Batch Data</h2>
378
- <p style='font-size: 1.1rem; margin-bottom: 2rem;'>Unggah file rekam jejak tweet (.txt) untuk diekstraksi dan dianalisis secara masal terhadap volatilitas pasar.</p>
379
  </div>
380
  """, unsafe_allow_html=True)
381
 
382
- tweet_files = st.file_uploader("Pilih file Tweet (.txt)", type=['txt'], accept_multiple_files=True)
383
 
384
- with st.expander("πŸ“Œ Format TXT yang Didukung"):
385
  st.code("username | 2024-03-01 14:00:00\nIsi tweet baris pertama di sini\n\nusername2 | 2024-03-01 15:30:00\nIsi tweet baris kedua di sini", language="text")
386
 
387
  st.markdown("<br>", unsafe_allow_html=True)
@@ -463,17 +414,17 @@ elif st.session_state.page == "analisis_batch":
463
  st.error("❌ Data kosong. Pastikan format penulisan TXT benar dan tweet berbahasa Inggris.")
464
  else:
465
  # Metrics Dashboard
466
- st.markdown("### πŸ“Š Ringkasan Pemrosesan")
467
  col_metric1, col_metric2, col_metric3 = st.columns(3)
468
- col_metric1.metric("Tweet Diproses", f"{total_tweets_uploaded}", border=True)
469
- col_metric2.metric("Tweet Diabaikan (Non-EN)", f"{total_tweets_skipped}", border=True)
470
- col_metric3.metric("Total Model", "5 NLP Models", border=True)
471
 
472
  target_dates = sorted(df['date'].unique())
473
  start_unix = int(datetime.strptime(target_dates[0], "%Y-%m-%d").replace(tzinfo=timezone.utc).timestamp()) - 86400
474
  end_unix = int(datetime.strptime(target_dates[-1], "%Y-%m-%d").replace(tzinfo=timezone.utc).timestamp()) + 86400
475
 
476
- st.info("πŸ“‘ Mengambil data harga Bitcoin dari CoinGecko API...")
477
 
478
  url = "https://api.coingecko.com/api/v3/coins/bitcoin/market_chart/range"
479
  params = {"vs_currency": "usd", "from": start_unix, "to": end_unix}
 
23
  # SETTING PATH ABSOLUT UNTUK GAMBAR
24
  # ==============================
25
  BASE_DIR = os.path.dirname(os.path.abspath(__file__))
 
26
  img_hero = os.path.join(BASE_DIR, "crypto-currency-concept-830px.png")
27
  img_batch = os.path.join(BASE_DIR, "slice3-1-1536x830.png")
28
 
29
  # ==============================
30
+ # KONFIGURASI HALAMAN
31
  # ==============================
32
  st.set_page_config(page_title="SKRIPSI - Sentimen BTC", page_icon="β‚Ώ", layout="wide", initial_sidebar_state="collapsed")
33
 
 
35
  st.session_state.page = "uji_kalimat"
36
 
37
  # ==============================
38
+ # "CSS HACKING" - VANCOUVER BITCOIN VIBE (ORANGE BLUR)
39
  # ==============================
40
  st.markdown("""
41
  <style>
42
+ /* Mengimpor Font Inter (Standar Industri Web Web3/Crypto) */
43
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
44
 
45
+ /* Menyembunyikan elemen bawaan Streamlit */
46
  #MainMenu {visibility: hidden;}
47
  footer {visibility: hidden;}
48
  header {visibility: hidden;}
49
 
50
+ /* Pengaturan Font & Background Global */
51
+ html, body, [class*="css"] {
52
+ font-family: 'Inter', sans-serif !important;
53
+ color: #0F172A !important; /* Warna teks gelap utama */
54
+ }
55
+
56
+ /* BACKGROUND HACK: Efek "Ngeblur Sedikit Opacity" Warna Oranye.
57
+ Menggunakan radial-gradient untuk menciptakan pendaran (glow) di latar belakang putih.
58
+ */
59
+ .stApp {
60
+ background-color: #FAFAFC;
61
+ background-image:
62
+ radial-gradient(circle at 10% 10%, rgba(247, 147, 26, 0.12) 0%, transparent 40%),
63
+ radial-gradient(circle at 90% 90%, rgba(247, 147, 26, 0.08) 0%, transparent 40%),
64
+ radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.8) 0%, transparent 100%);
65
+ background-attachment: fixed;
66
+ }
67
+
68
+ /* Padding Kontainer Utama */
69
  .block-container {
70
  padding-top: 2rem !important;
71
+ padding-bottom: 3rem !important;
72
  max-width: 1200px !important;
73
  }
74
 
75
+ /* STYLING TOMBOL STREAMLIT (Dibuat Mirip Tombol Website Asli)
76
+ */
77
+ div[data-testid="stButton"] > button {
78
+ background-color: #F7931A !important; /* Orange Bitcoin */
79
+ color: #FFFFFF !important;
80
+ border: none !important;
81
+ border-radius: 8px !important;
82
+ font-weight: 600 !important;
83
+ padding: 0.6rem 1.8rem !important;
84
+ box-shadow: 0 4px 10px rgba(247, 147, 26, 0.25) !important;
85
+ transition: all 0.3s ease !important;
86
+ }
87
+ div[data-testid="stButton"] > button:hover {
88
+ background-color: #E68310 !important;
89
+ transform: translateY(-2px) !important;
90
+ box-shadow: 0 6px 15px rgba(247, 147, 26, 0.35) !important;
91
  }
92
 
93
+ /* STYLING TEXT AREA (Glassmorphism & Clean Input)
94
+ */
95
+ .stTextArea textarea {
96
+ background-color: rgba(255, 255, 255, 0.8) !important;
97
+ backdrop-filter: blur(5px);
98
+ color: #0F172A !important;
99
+ border: 1px solid #CBD5E1 !important;
100
+ border-radius: 10px !important;
101
+ font-size: 1.05rem;
102
+ padding: 1.2rem !important;
103
+ box-shadow: inset 0 2px 4px rgba(0,0,0,0.02) !important;
104
  }
105
+ .stTextArea textarea:focus {
106
+ border-color: #F7931A !important;
107
+ box-shadow: 0 0 0 2px rgba(247, 147, 26, 0.3) !important;
108
+ }
109
+
110
+ /* STYLING METRIK KARTU (Tab Analisis Batch)
111
+ */
112
+ div[data-testid="metric-container"] {
113
+ background-color: rgba(255, 255, 255, 0.95) !important;
114
+ backdrop-filter: blur(10px);
115
+ border: 1px solid #E2E8F0 !important;
116
+ border-left: 5px solid #F7931A !important; /* Garis Oranye Kiri */
117
+ padding: 1.2rem !important;
118
+ border-radius: 12px !important;
119
+ box-shadow: 0 4px 15px rgba(0,0,0,0.03) !important;
120
+ }
121
+ div[data-testid="metric-container"] label {
122
+ color: #64748B !important;
123
+ font-weight: 500 !important;
124
+ }
125
+ div[data-testid="metric-container"] div {
126
+ color: #0F172A !important;
127
+ font-weight: 800 !important;
128
+ }
129
+
130
+ /* Dataframe & Table Modern */
131
+ div[data-testid="stDataFrame"] {
132
+ background-color: white;
133
+ border: 1px solid #E2E8F0;
134
+ border-radius: 12px;
135
+ box-shadow: 0 4px 10px rgba(0,0,0,0.02);
136
  }
137
 
138
+ /* Header Typography */
139
+ h1, h2, h3, h4, h5 {
140
+ color: #0F172A !important;
141
+ font-weight: 800 !important;
142
+ letter-spacing: -0.5px !important;
143
+ }
144
+ p, span, label {
145
+ color: #334155 !important;
146
  }
147
  </style>
148
  """, unsafe_allow_html=True)
149
 
150
  # ==============================
151
+ # HEADER & NAVIGASI (Dibuat ala Navbar Web)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  # ==============================
153
  def set_page(page_name):
154
  st.session_state.page = page_name
155
 
156
+ st.markdown("<div style='padding-bottom: 0.5rem;'></div>", unsafe_allow_html=True)
157
+ col_logo, col_space, col_nav1, col_nav2 = st.columns([3, 4, 1.5, 1.5], gap="small")
158
 
159
  with col_logo:
160
+ # Logo text Vancouver Bitcoin style
161
+ st.markdown("<h3 style='margin:0; padding-top:5px; font-weight:800; color:#0F172A !important;'><span style='color:#F7931A;'>β‚Ώitcoin</span> Sentimen</h3>", unsafe_allow_html=True)
 
162
 
163
  with col_nav1:
164
+ if st.button("πŸ“ Uji Kalimat", use_container_width=True, key="nav_uji"):
 
 
165
  set_page("uji_kalimat")
166
  st.rerun()
 
167
 
168
  with col_nav2:
169
+ if st.button("πŸ“Š Analisis Batch", use_container_width=True, key="nav_batch"):
 
 
170
  set_page("analisis_batch")
171
  st.rerun()
 
172
 
173
+ # Divider bawah navbar
174
+ st.markdown("<hr style='border: none; height: 1px; background-color: #E2E8F0; margin-top: 10px; margin-bottom: 40px;'>", unsafe_allow_html=True)
175
 
176
 
177
  # ==============================
178
+ # DOWNLOAD RESOURCES & LOAD MODELS
179
  # ==============================
180
  @st.cache_resource
181
  def download_nltk_resources():
 
195
  roberta_large = pipeline("sentiment-analysis", model="siebert/sentiment-roberta-large-english", device=-1, truncation=True, max_length=512)
196
  return vader, bertweet, roberta, roberta_large
197
 
198
+ with st.spinner('Mempersiapkan mesin AI NLP...'):
199
  vader, bertweet, roberta, roberta_large = load_all_models()
200
 
201
  # ==============================
202
+ # FUNGSI CLEAN TEXT & MAPPING
203
  # ==============================
204
  def clean_text(text):
205
  text = str(text).lower()
 
229
 
230
 
231
  # ==============================================================================
232
+ # HALAMAN 1: UJI KALIMAT (Mirip Gambar 1 - Tema Terang Oranye)
233
  # ==============================================================================
234
  if st.session_state.page == "uji_kalimat":
235
  col_text, col_img = st.columns([1.1, 1], gap="large")
236
 
237
  with col_text:
238
  st.markdown("""
239
+ <div style="padding-top: 1rem;">
240
+ <h1 style="font-size: 3.5rem; line-height: 1.1; margin-bottom: 1rem; font-weight: 800; letter-spacing: -1.5px; color: #0F172A;">
241
  Bitcoin Volatility <br><span style="color: #F7931A;">vs Public Sentiment</span>
242
  </h1>
243
+ <p style='font-size: 1.15rem; font-weight: 400; color: #475569 !important; margin-bottom: 2rem;'>
244
  Analisis Volatilitas Harga Bitcoin Terhadap Sentimen Publik Pada Platform X Berbasis Python.
245
  </p>
246
+ <div style="background-color: #FFFFFF; border: 1px solid #E2E8F0; padding: 15px; border-radius: 8px; margin-bottom: 2.5rem; box-shadow: 0 2px 10px rgba(0,0,0,0.02);">
247
+ <p style="margin: 0; font-size: 0.95rem; color: #334155 !important;">
248
+ <span style="color: #F7931A;">πŸ“Œ</span> <b>Peneliti:</b> Arya Galuh Saputra (H1D022022)
249
  </p>
250
  </div>
251
  </div>
252
  """, unsafe_allow_html=True)
253
 
254
+ user_input = st.text_area("✍️ Masukkan Tweet (Bahasa Inggris):", "Great, Bitcoin just crashed another 10% today.", height=120)
255
 
256
  st.markdown("<br>", unsafe_allow_html=True)
257
  analyze_btn = st.button("πŸš€ Analisis Sentimen Sekarang", use_container_width=True)
 
260
  try:
261
  st.image(img_hero, use_container_width=True)
262
  except Exception:
263
+ st.info(f"Visualisasi Hero akan muncul di sini. (Pastikan file {os.path.basename(img_hero)} tersedia di folder yang sama)")
264
 
265
  if analyze_btn:
266
+ st.markdown("<br><hr style='border-color: #E2E8F0;'><br>", unsafe_allow_html=True)
267
+ st.markdown("<h3 style='text-align: center; margin-bottom: 2rem; color: #0F172A;'>πŸ“‹ Hasil Deteksi Sentimen</h3>", unsafe_allow_html=True)
268
 
269
  try:
270
  if detect(user_input) != 'en':
271
  st.warning("⚠️ Teks sepertinya bukan bahasa Inggris. Hasil prediksi mungkin memiliki bias.")
272
+ except: pass
 
273
 
274
  text = clean_text(user_input)
275
 
 
305
 
306
 
307
  # ==============================================================================
308
+ # HALAMAN 2: ANALISIS BATCH DATA (Mirip Gambar 2 - Dashboard Modern)
309
  # ==============================================================================
310
  elif st.session_state.page == "analisis_batch":
311
+ # Tema Matplotlib/Seaborn disesuaikan dengan latar terang transparan
312
  plt.style.use('default')
313
  sns.set_theme(style="whitegrid", rc={
314
+ "axes.facecolor": "rgba(255,255,255,0.7)",
315
+ "figure.facecolor": "rgba(255,255,255,0)",
316
  "axes.edgecolor": "#E2E8F0",
317
  "text.color": "#0F172A",
318
  "xtick.color": "#64748B",
 
325
  with col_upload:
326
  st.markdown("""
327
  <div style="padding-top: 1rem;">
328
+ <h2 style="font-size: 2.5rem; margin-bottom: 0.5rem; font-weight: 800; letter-spacing: -1px; color: #0F172A;">Analisis Batch Data</h2>
329
+ <p style='font-size: 1.1rem; color: #475569 !important; margin-bottom: 1.5rem;'>Unggah file rekam jejak tweet (.txt) untuk diekstraksi dan dianalisis secara masal terhadap volatilitas pasar.</p>
330
  </div>
331
  """, unsafe_allow_html=True)
332
 
333
+ tweet_files = st.file_uploader("πŸ“‚ Pilih file Tweet (.txt)", type=['txt'], accept_multiple_files=True)
334
 
335
+ with st.expander("πŸ“Œ Format TXT yang Didukung (Klik untuk melihat)"):
336
  st.code("username | 2024-03-01 14:00:00\nIsi tweet baris pertama di sini\n\nusername2 | 2024-03-01 15:30:00\nIsi tweet baris kedua di sini", language="text")
337
 
338
  st.markdown("<br>", unsafe_allow_html=True)
 
414
  st.error("❌ Data kosong. Pastikan format penulisan TXT benar dan tweet berbahasa Inggris.")
415
  else:
416
  # Metrics Dashboard
417
+ st.markdown("<h3 style='margin-bottom: 1.5rem;'>πŸ“Š Ringkasan Pemrosesan</h3>", unsafe_allow_html=True)
418
  col_metric1, col_metric2, col_metric3 = st.columns(3)
419
+ col_metric1.metric("Tweet Berhasil Diproses", f"{total_tweets_uploaded}", border=False)
420
+ col_metric2.metric("Tweet Diabaikan (Non-EN)", f"{total_tweets_skipped}", border=False)
421
+ col_metric3.metric("Total Model NLP", "5 Models", border=False)
422
 
423
  target_dates = sorted(df['date'].unique())
424
  start_unix = int(datetime.strptime(target_dates[0], "%Y-%m-%d").replace(tzinfo=timezone.utc).timestamp()) - 86400
425
  end_unix = int(datetime.strptime(target_dates[-1], "%Y-%m-%d").replace(tzinfo=timezone.utc).timestamp()) + 86400
426
 
427
+ st.info("πŸ“‘ Menarik data harga Bitcoin dari CoinGecko API...")
428
 
429
  url = "https://api.coingecko.com/api/v3/coins/bitcoin/market_chart/range"
430
  params = {"vs_currency": "usd", "from": start_unix, "to": end_unix}