Reyvan22 commited on
Commit
6f5bc78
Β·
verified Β·
1 Parent(s): 1a3989e

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +423 -38
src/streamlit_app.py CHANGED
@@ -1,40 +1,425 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
  import streamlit as st
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
1
+ # === ClimatePulse: Chatbot Analisis Opini Publik ===
2
+ import torch
 
3
  import streamlit as st
4
+ import pandas as pd
5
+ import pydeck as pdk
6
+ import altair as alt
7
+ from transformers import pipeline, AutoTokenizer, AutoModelForTokenClassification, AutoModelForSequenceClassification
8
+ from geopy.geocoders import Nominatim
9
+ from datetime import datetime
10
+ import os
11
+
12
+ # === Setup Halaman ===
13
+ st.set_page_config(page_title="ClimatePulse", layout="centered")
14
+
15
+ # === Load Model & Pipeline ===
16
+ device = 0 if torch.cuda.is_available() else -1
17
+
18
+ # Sentimen
19
+ sent_tokenizer = AutoTokenizer.from_pretrained("mdhugol/indonesia-bert-sentiment-classification")
20
+ sent_model = AutoModelForSequenceClassification.from_pretrained("mdhugol/indonesia-bert-sentiment-classification")
21
+ pipe_sent = pipeline("sentiment-analysis", model=sent_model, tokenizer=sent_tokenizer)
22
+
23
+ # Emosi
24
+ pipe_emo = pipeline("sentiment-analysis", model="azizp128/prediksi-emosi-indobert", device=device)
25
+
26
+ # NER
27
+ ner_tokenizer = AutoTokenizer.from_pretrained("cahya/bert-base-indonesian-NER")
28
+ ner_model = AutoModelForTokenClassification.from_pretrained("cahya/bert-base-indonesian-NER")
29
+ pipe_ner = pipeline("ner", model=ner_model, tokenizer=ner_tokenizer, aggregation_strategy="simple")
30
+
31
+ label_map = {'LABEL_0': 'Positif', 'LABEL_1': 'Netral', 'LABEL_2': 'Negatif'}
32
+
33
+ # === Custom Dark Mode Style + Logo ===
34
+ page_bg = '''
35
+ <style>
36
+ [data-testid="stAppViewContainer"] {
37
+ background-color: #0e1525;
38
+ color: white;
39
+ }
40
+ [data-testid="stHeader"] {
41
+ background-color: rgba(0,0,0,0);
42
+ }
43
+ [data-testid="stSidebar"] > div:first-child {
44
+ background-color: #1f2937;
45
+ }
46
+ .block-container {
47
+ padding-top: 2rem;
48
+ padding-bottom: 2rem;
49
+ font-family: "Segoe UI", sans-serif;
50
+ }
51
+ h1, h2, h3, h4, h5 {
52
+ font-family: 'Segoe UI', sans-serif;
53
+ color: #10B981;
54
+ }
55
+ .stButton>button {
56
+ background-color: #10B981;
57
+ color: white;
58
+ border-radius: 8px;
59
+ padding: 0.5rem 1rem;
60
+ font-size: 1rem;
61
+ border: none;
62
+ }
63
+ .stTextInput>div>div>input, .stTextArea>div>textarea {
64
+ background-color: #1f2937;
65
+ color: white;
66
+ border-radius: 6px;
67
+ border: 1px solid #374151;
68
+ }
69
+ </style>
70
+ '''
71
+ st.markdown(page_bg, unsafe_allow_html=True)
72
+
73
+ # === Judul Halaman dengan Logo di Sebelah Teks ===
74
+ col1, col2 = st.columns([1, 8])
75
+ with col1:
76
+ st.image("logo.png", width=60)
77
+
78
+ with col2:
79
+ st.markdown("""
80
+ <div style='display: flex; flex-direction: column; justify-content: center;'>
81
+ <h4 style='color: #10B981; margin-bottom: 0;'>ClimatePulse - Analisis Opini SDG 13</h4>
82
+ <h1 style='color: white; margin-top: 0;'>Perubahan Iklim di Media Sosial</h1>
83
+ <p style='color: gray;'>Telusuri opini publik, sentimen, emosi, dan entitas terkait kebijakan dan bencana iklim</p>
84
+ </div>
85
+ """, unsafe_allow_html=True)
86
+
87
+ # === Form Input User ===
88
+ with st.form(key="input_form"):
89
+ text_input = st.text_area("Input Teks / Tweet", placeholder="Contoh: PLTN dibangun di Papua, saya takut dan kecewa", height=120)
90
+ submit = st.form_submit_button("πŸ” ANALISIS")
91
+
92
+ # === Analisis dan Visualisasi Lain Tetap ===
93
+ # (seluruh isi kode berikutnya tetap seperti sebelumnya)
94
+ # === Tidak ditampilkan ulang agar tidak duplikasi ===
95
+
96
+
97
+
98
+ # === Analisis dan Visualisasi Lain Tetap ===
99
+ # (seluruh isi kode berikutnya tetap seperti sebelumnya)
100
+ # === Tidak ditampilkan ulang agar tidak duplikasi ===
101
+
102
+
103
+ # === Proses Analisis Tunggal ===
104
+ if submit and text_input.strip():
105
+ with st.spinner("Menganalisis opini publik..."):
106
+ sent = pipe_sent(text_input)[0]
107
+ sent_label = label_map.get(sent['label'], sent['label'])
108
+ emo = pipe_emo(text_input)[0]['label'].capitalize()
109
+ ner = pipe_ner(text_input)
110
+ ents = [e['word'] for e in ner]
111
+
112
+ lokasi_kunci = [
113
+ # === Wilayah Umum / Pulau ===
114
+ "sumatera", "jawa", "kalimantan", "sulawesi", "papua", "maluku", "nusa tenggara", "kepulauan seribu",
115
+
116
+ # === Nama Provinsi Lengkap (38) ===
117
+ "aceh", "sumatera utara", "sumatera barat", "riau", "kepulauan riau", "jambi", "bengkulu",
118
+ "sumatera selatan", "bangka belitung", "lampung",
119
+ "banten", "dki jakarta", "jawa barat", "jawa tengah", "daerah istimewa yogyakarta", "jawa timur",
120
+ "bali", "nusa tenggara barat", "nusa tenggara timur",
121
+ "kalimantan barat", "kalimantan tengah", "kalimantan selatan", "kalimantan timur", "kalimantan utara",
122
+ "sulawesi utara", "sulawesi tengah", "sulawesi selatan", "sulawesi tenggara", "gorontalo", "sulawesi barat",
123
+ "maluku", "maluku utara",
124
+ "papua", "papua barat", "papua selatan", "papua tengah", "papua pegunungan", "papua barat daya",
125
+
126
+ # === Ibu Kota Provinsi ===
127
+ "banda aceh", "medan", "padang", "pekanbaru", "tanjungpinang", "jambi", "bengkulu",
128
+ "palembang", "pangkalpinang", "bandar lampung",
129
+ "serang", "jakarta", "bandung", "semarang", "yogyakarta", "surabaya",
130
+ "denpasar", "mataram", "kupang",
131
+ "pontianak", "palangka raya", "banjarmasin", "samarinda", "tarakan",
132
+ "manado", "palu", "makassar", "kendari", "gorontalo", "mamuju",
133
+ "ambon", "ternate",
134
+ "jayapura", "manokwari", "merauke", "nabire", "wamena", "fakfak", "sorong", "timika",
135
+
136
+ # === Kota/Kabupaten Besar atau Strategis ===
137
+ "bekasi", "bogor", "depok", "tangerang", "cirebon", "tegal", "purwokerto", "solo", "magelang",
138
+ "malang", "kediri", "sidoarjo", "pasuruan", "probolinggo", "lumajang", "blitar", "jember",
139
+ "banyuwangi", "cilacap", "padangsidimpuan", "binjai", "sibolga", "lubuklinggau", "palopo",
140
+ "parepare", "bitung", "tomohon", "kotamobagu", "kotabaru", "pangkalan bun", "ketapang",
141
+ "palu", "baubau", "karangasem", "buleleng", "labuan bajo", "ende", "bima", "dompu",
142
+
143
+ # === Lokasi Baru / Khusus / Otorita ===
144
+ "nusantara", # Ibu kota negara baru di Kaltim
145
+ "penajam paser utara", "balikpapan", "samarinda", "bontang", # Kaltim area
146
+ "kepri", "ntb", "ntt", "kaltim", "kalteng", "kalsel", "kalbar", "kaltara", # singkatan populer
147
+
148
+ # === Lokasi Adat/Kultural (yang sering disebut) ===
149
+ "minangkabau", "batak", "dayak", "asmat", "ambon", "bugis", "toraja", "sunda", "madura", "tapanuli"
150
+ ]
151
+
152
+ locs = []
153
+ for e in ner:
154
+ ent_text = e['word'].lower()
155
+ if e['entity_group'] == 'LOC':
156
+ locs.append(e['word'])
157
+ else:
158
+ for keyword in lokasi_kunci:
159
+ if keyword in ent_text:
160
+ locs.append(keyword.capitalize())
161
+ locs = list(set(locs))
162
+
163
+ geolocator = Nominatim(user_agent="climatepulse")
164
+ geo_locs = []
165
+ for loc in locs:
166
+ try:
167
+ location = geolocator.geocode(loc)
168
+ if location:
169
+ geo_locs.append({
170
+ 'lokasi': loc,
171
+ 'lat': location.latitude,
172
+ 'lon': location.longitude,
173
+ 'jumlah': 1
174
+ })
175
+ except:
176
+ continue
177
+
178
+ now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
179
+ df_log_single = pd.DataFrame([{
180
+ "timestamp": now,
181
+ "text": text_input,
182
+ "sentimen": sent_label,
183
+ "emosi": emo
184
+ }])
185
+
186
+ log_file = "log_tren.csv"
187
+ if os.path.exists(log_file):
188
+ pd.concat([pd.read_csv(log_file), df_log_single]).to_csv(log_file, index=False)
189
+ else:
190
+ df_log_single.to_csv(log_file, index=False)
191
+
192
+
193
+ emoji_map = {
194
+ "Senang": "😊",
195
+ "Sedih": "😒",
196
+ "Marah": "😑",
197
+ "Takut": "😨",
198
+ "Kecewa": "😞",
199
+ "Netral": "😐"
200
+ }
201
+ # === Tampilkan Hasil ===
202
+
203
+ st.markdown(f"""
204
+ <div style='background-color: #1f2937; padding: 1rem; border-radius: 10px;'>
205
+ <h3 style='color: white;'>Hasil Analisis</h3>
206
+ <p><b>Sentimen:</b> <span style='color: red;'>{sent_label}</span>  | 
207
+ <b>Emosi:</b> <span style='color: #facc15;'>{emo} {emoji_map.get(emo, '')}</span></p>
208
+ <p><b>πŸ“ Lokasi:</b> {', '.join(locs) or "Tidak ditemukan"}</p>
209
+ <p><b>πŸ”– Entitas:</b> {', '.join(ents) or "Tidak ditemukan"}</p>
210
+ </div>
211
+ """, unsafe_allow_html=True)
212
+
213
+ # === Tambahan: Peta Opini Publik berdasarkan Log ===
214
+ if os.path.exists("log_tren.csv"):
215
+ df_log = pd.read_csv("log_tren.csv")
216
+ lokasi_kunci = [
217
+ # === Wilayah Umum / Pulau ===
218
+ "sumatera", "jawa", "kalimantan", "sulawesi", "papua", "maluku", "nusa tenggara", "kepulauan seribu",
219
+
220
+ # === Nama Provinsi Lengkap (38) ===
221
+ "aceh", "sumatera utara", "sumatera barat", "riau", "kepulauan riau", "jambi", "bengkulu",
222
+ "sumatera selatan", "bangka belitung", "lampung",
223
+ "banten", "dki jakarta", "jawa barat", "jawa tengah", "daerah istimewa yogyakarta", "jawa timur",
224
+ "bali", "nusa tenggara barat", "nusa tenggara timur",
225
+ "kalimantan barat", "kalimantan tengah", "kalimantan selatan", "kalimantan timur", "kalimantan utara",
226
+ "sulawesi utara", "sulawesi tengah", "sulawesi selatan", "sulawesi tenggara", "gorontalo", "sulawesi barat",
227
+ "maluku", "maluku utara",
228
+ "papua", "papua barat", "papua selatan", "papua tengah", "papua pegunungan", "papua barat daya",
229
+
230
+ # === Ibu Kota Provinsi ===
231
+ "banda aceh", "medan", "padang", "pekanbaru", "tanjungpinang", "jambi", "bengkulu",
232
+ "palembang", "pangkalpinang", "bandar lampung",
233
+ "serang", "jakarta", "bandung", "semarang", "yogyakarta", "surabaya",
234
+ "denpasar", "mataram", "kupang",
235
+ "pontianak", "palangka raya", "banjarmasin", "samarinda", "tarakan",
236
+ "manado", "palu", "makassar", "kendari", "gorontalo", "mamuju",
237
+ "ambon", "ternate",
238
+ "jayapura", "manokwari", "merauke", "nabire", "wamena", "fakfak", "sorong", "timika",
239
+
240
+ # === Kota/Kabupaten Besar atau Strategis ===
241
+ "bekasi", "bogor", "depok", "tangerang", "cirebon", "tegal", "purwokerto", "solo", "magelang",
242
+ "malang", "kediri", "sidoarjo", "pasuruan", "probolinggo", "lumajang", "blitar", "jember",
243
+ "banyuwangi", "cilacap", "padangsidimpuan", "binjai", "sibolga", "lubuklinggau", "palopo",
244
+ "parepare", "bitung", "tomohon", "kotamobagu", "kotabaru", "pangkalan bun", "ketapang",
245
+ "palu", "baubau", "karangasem", "buleleng", "labuan bajo", "ende", "bima", "dompu",
246
+
247
+ # === Lokasi Baru / Khusus / Otorita ===
248
+ "nusantara", # Ibu kota negara baru di Kaltim
249
+ "penajam paser utara", "balikpapan", "samarinda", "bontang", # Kaltim area
250
+ "kepri", "ntb", "ntt", "kaltim", "kalteng", "kalsel", "kalbar", "kaltara", # singkatan populer
251
+
252
+ # === Lokasi Adat/Kultural (yang sering disebut) ===
253
+ "minangkabau", "batak", "dayak", "asmat", "ambon", "bugis", "toraja", "sunda", "madura", "tapanuli"
254
+ ]
255
+ lokasi_counter = {}
256
+ for text in df_log['text']:
257
+ for keyword in lokasi_kunci:
258
+ if keyword in text.lower():
259
+ lokasi = keyword.capitalize()
260
+ lokasi_counter[lokasi] = lokasi_counter.get(lokasi, 0) + 1
261
+
262
+ geo_locs = []
263
+ geolocator = Nominatim(user_agent="climatepulse-map")
264
+ for lokasi, jumlah in lokasi_counter.items():
265
+ try:
266
+ location = geolocator.geocode(lokasi)
267
+ if location:
268
+ geo_locs.append({
269
+ 'lokasi': lokasi,
270
+ 'lat': location.latitude,
271
+ 'lon': location.longitude,
272
+ 'jumlah': jumlah
273
+ })
274
+ except:
275
+ continue
276
+
277
+ if geo_locs:
278
+ map_df = pd.DataFrame(geo_locs)
279
+ st.markdown("### πŸ—ΊοΈ Peta Opini Publik")
280
+ st.pydeck_chart(pdk.Deck(
281
+ map_style=None,
282
+ initial_view_state=pdk.ViewState(latitude=-2.5, longitude=117.0, zoom=4, pitch=0),
283
+ layers=[
284
+ pdk.Layer(
285
+ "ScatterplotLayer",
286
+ data=map_df,
287
+ get_position='[lon, lat]',
288
+ get_color='[255, 100, 100, 160]',
289
+ get_radius='jumlah * 10000',
290
+ pickable=True,
291
+ auto_highlight=True
292
+ )
293
+ ],
294
+ tooltip={"text": "{lokasi}: {jumlah} opini"}
295
+ ))
296
+ else:
297
+ st.info("❗ Tidak ada lokasi yang berhasil dipetakan dari histori log.")
298
+
299
+
300
+ st.markdown("### πŸ“ˆ Tren Waktu Sentimen")
301
+ if os.path.exists("log_tren.csv"):
302
+ df_log = pd.read_csv("log_tren.csv")
303
+ df_log['timestamp'] = pd.to_datetime(df_log['timestamp'])
304
+ df_log['tanggal'] = df_log['timestamp'].dt.date
305
+ trend_all = df_log.groupby(['tanggal', 'sentimen']).size().reset_index(name='jumlah')
306
+ chart = alt.Chart(trend_all).mark_line(point=True).encode(
307
+ x='tanggal:T',
308
+ y='jumlah:Q',
309
+ color='sentimen:N'
310
+ ).properties(width=600)
311
+ st.altair_chart(chart, use_container_width=True)
312
+
313
+ # === Upload CSV untuk Analisis Massal ===
314
+ st.markdown("---")
315
+ st.markdown("### πŸ“₯ Analisis CSV Massal")
316
+ uploaded_file = st.file_uploader("Upload file CSV berisi kolom 'text'", type=["csv"])
317
+
318
+ if uploaded_file is not None:
319
+ df_csv = pd.read_csv(uploaded_file)
320
+ st.write("Pratinjau Data:", df_csv.head())
321
+
322
+ if "text" in df_csv.columns:
323
+ result_data = []
324
+ geo_locs = []
325
+ log_rows = []
326
+ lokasi_kunci = [
327
+ # === Wilayah Umum / Pulau ===
328
+ "sumatera", "jawa", "kalimantan", "sulawesi", "papua", "maluku", "nusa tenggara", "kepulauan seribu",
329
+
330
+ # === Nama Provinsi Lengkap (38) ===
331
+ "aceh", "sumatera utara", "sumatera barat", "riau", "kepulauan riau", "jambi", "bengkulu",
332
+ "sumatera selatan", "bangka belitung", "lampung",
333
+ "banten", "dki jakarta", "jawa barat", "jawa tengah", "daerah istimewa yogyakarta", "jawa timur",
334
+ "bali", "nusa tenggara barat", "nusa tenggara timur",
335
+ "kalimantan barat", "kalimantan tengah", "kalimantan selatan", "kalimantan timur", "kalimantan utara",
336
+ "sulawesi utara", "sulawesi tengah", "sulawesi selatan", "sulawesi tenggara", "gorontalo", "sulawesi barat",
337
+ "maluku", "maluku utara",
338
+ "papua", "papua barat", "papua selatan", "papua tengah", "papua pegunungan", "papua barat daya",
339
+
340
+ # === Ibu Kota Provinsi ===
341
+ "banda aceh", "medan", "padang", "pekanbaru", "tanjungpinang", "jambi", "bengkulu",
342
+ "palembang", "pangkalpinang", "bandar lampung",
343
+ "serang", "jakarta", "bandung", "semarang", "yogyakarta", "surabaya",
344
+ "denpasar", "mataram", "kupang",
345
+ "pontianak", "palangka raya", "banjarmasin", "samarinda", "tarakan",
346
+ "manado", "palu", "makassar", "kendari", "gorontalo", "mamuju",
347
+ "ambon", "ternate",
348
+ "jayapura", "manokwari", "merauke", "nabire", "wamena", "fakfak", "sorong", "timika",
349
+
350
+ # === Kota/Kabupaten Besar atau Strategis ===
351
+ "bekasi", "bogor", "depok", "tangerang", "cirebon", "tegal", "purwokerto", "solo", "magelang",
352
+ "malang", "kediri", "sidoarjo", "pasuruan", "probolinggo", "lumajang", "blitar", "jember",
353
+ "banyuwangi", "cilacap", "padangsidimpuan", "binjai", "sibolga", "lubuklinggau", "palopo",
354
+ "parepare", "bitung", "tomohon", "kotamobagu", "kotabaru", "pangkalan bun", "ketapang",
355
+ "palu", "baubau", "karangasem", "buleleng", "labuan bajo", "ende", "bima", "dompu",
356
+
357
+ # === Lokasi Baru / Khusus / Otorita ===
358
+ "nusantara", # Ibu kota negara baru di Kaltim
359
+ "penajam paser utara", "balikpapan", "samarinda", "bontang", # Kaltim area
360
+ "kepri", "ntb", "ntt", "kaltim", "kalteng", "kalsel", "kalbar", "kaltara", # singkatan populer
361
+
362
+ # === Lokasi Adat/Kultural (yang sering disebut) ===
363
+ "minangkabau", "batak", "dayak", "asmat", "ambon", "bugis", "toraja", "sunda", "madura", "tapanuli"
364
+ ]
365
+
366
+ geolocator = Nominatim(user_agent="climatepulse")
367
+
368
+ for i, row in df_csv.iterrows():
369
+ text = str(row["text"])
370
+ sent = pipe_sent(text)[0]
371
+ sent_label = label_map.get(sent['label'], sent['label'])
372
+ emo = pipe_emo(text)[0]['label'].capitalize()
373
+ ner = pipe_ner(text)
374
+ ents = [e['word'] for e in ner]
375
+
376
+ locs = []
377
+ for e in ner:
378
+ ent_text = e['word'].lower()
379
+ if e['entity_group'] == 'LOC':
380
+ locs.append(e['word'])
381
+ else:
382
+ for keyword in lokasi_kunci:
383
+ if keyword in ent_text:
384
+ locs.append(keyword.capitalize())
385
+ locs = list(set(locs))
386
+
387
+ for loc in locs:
388
+ try:
389
+ location = geolocator.geocode(loc)
390
+ if location:
391
+ geo_locs.append({
392
+ 'lokasi': loc,
393
+ 'lat': location.latitude,
394
+ 'lon': location.longitude,
395
+ 'jumlah': 1
396
+ })
397
+ except:
398
+ continue
399
+
400
+ now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
401
+ log_rows.append({"timestamp": now, "text": text, "sentimen": sent_label, "emosi": emo})
402
+
403
+ result_data.append({
404
+ "text": text,
405
+ "sentimen": sent_label,
406
+ "emosi": emo,
407
+ "entitas": ", ".join(ents)
408
+ })
409
+
410
+ df_result = pd.DataFrame(result_data)
411
+ st.success("Analisis selesai!")
412
+ st.dataframe(df_result)
413
+
414
+ csv_download = df_result.to_csv(index=False).encode('utf-8')
415
+ st.download_button("πŸ“₯ Download Hasil CSV", csv_download, "hasil_analisis.csv", "text/csv")
416
+
417
+ log_file = "log_tren.csv"
418
+ df_log_append = pd.DataFrame(log_rows)
419
+ if os.path.exists(log_file):
420
+ pd.concat([pd.read_csv(log_file), df_log_append]).to_csv(log_file, index=False)
421
+ else:
422
+ df_log_append.to_csv(log_file, index=False)
423
+
424
 
425
+