From f3b1f5fc846f69345ea6f859e6e43d6ca5e7693b Mon Sep 17 00:00:00 2001 From: el Date: Sun, 10 May 2026 13:36:22 +0800 Subject: [PATCH] Initial commit --- .gitignore | 27 + PERBAIKAN_REALTIME_DATA.md | 235 +++++ app.py | 982 ++++++++++++++++++++ backup_data_sentimen_komdigi.db | Bin 0 -> 40960 bytes crawler.py | 137 +++ data_sentimen_komdigi.db | Bin 0 -> 172032 bytes database.py | 157 ++++ hasil_analisis_sentimen.xlsx | Bin 0 -> 62807 bytes hasil_prediksi_sentimen.xlsx | Bin 0 -> 53357 bytes import_data_awal.py | 113 +++ indonesian-stopwords-complete.txt | 758 ++++++++++++++++ labeled_tweet.xlsx | Bin 0 -> 45279 bytes model_naive_bayes.pkl | Bin 0 -> 31559 bytes page_modules/__init__.py | 0 page_modules/crawling_page.py | 1350 +++++++++++++++++++++++++++ page_modules/preprocessing_page.py | 700 ++++++++++++++ page_modules/sentiment_page.py | 1356 ++++++++++++++++++++++++++++ page_modules/table_utils.py | 271 ++++++ requirements.txt | 15 + run_scraper_loop.py | 4 + scraper_worker.py | 364 ++++++++ sentiment_service.py | 39 + tfidf_vectorizer.pkl | Bin 0 -> 37921 bytes timezone_utils.py | 224 +++++ tweets-data/hasil.csv | 21 + tweets-data/hasil.old.csv | 21 + tweets-data/hasil_latest.csv | 1 + tweets-data/hasil_latest.old.csv | 1 + tweets-data/hasil_top.old.csv | 8 + 29 files changed, 6784 insertions(+) create mode 100644 .gitignore create mode 100644 PERBAIKAN_REALTIME_DATA.md create mode 100644 app.py create mode 100644 backup_data_sentimen_komdigi.db create mode 100644 crawler.py create mode 100644 data_sentimen_komdigi.db create mode 100644 database.py create mode 100644 hasil_analisis_sentimen.xlsx create mode 100644 hasil_prediksi_sentimen.xlsx create mode 100644 import_data_awal.py create mode 100644 indonesian-stopwords-complete.txt create mode 100644 labeled_tweet.xlsx create mode 100644 model_naive_bayes.pkl create mode 100644 page_modules/__init__.py create mode 100644 page_modules/crawling_page.py create mode 100644 page_modules/preprocessing_page.py create mode 100644 page_modules/sentiment_page.py create mode 100644 page_modules/table_utils.py create mode 100644 requirements.txt create mode 100644 run_scraper_loop.py create mode 100644 scraper_worker.py create mode 100644 sentiment_service.py create mode 100644 tfidf_vectorizer.pkl create mode 100644 timezone_utils.py create mode 100644 tweets-data/hasil.csv create mode 100644 tweets-data/hasil.old.csv create mode 100644 tweets-data/hasil_latest.csv create mode 100644 tweets-data/hasil_latest.old.csv create mode 100644 tweets-data/hasil_top.old.csv diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cfe1281 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +.DS_Store + +# Python caches +__pycache__/ +*.py[cod] +*$py.class + +# Virtual environments +venv/ +.venv/ +env/ + +# Environment and local secrets +.env +.env.* +!.env.example + +# Streamlit/local runtime +.streamlit/secrets.toml + +# App runtime state/logs +auto_crawl_log.json +crawler_state.json +*.log + +# Notebook/IPython +.ipynb_checkpoints/ diff --git a/PERBAIKAN_REALTIME_DATA.md b/PERBAIKAN_REALTIME_DATA.md new file mode 100644 index 0000000..49eb3db --- /dev/null +++ b/PERBAIKAN_REALTIME_DATA.md @@ -0,0 +1,235 @@ +# πŸ“Š Perbaikan Dashboard Sentimen - Data Realtime + +## 🎯 Ringkasan Perubahan + +Semua fungsi di dashboard telah diperbaiki untuk **menggunakan data realtime yang dinamis** bukan data dummy. Sistem sekarang secara otomatis merefresh data ketika ada perubahan di database dan menampilkan timestamp setiap kali data diupdate. + +--- + +## πŸ”„ Perbaikan Utama + +### 1. **Database Functions** (`database.py`) +βœ… Ditambahkan helper functions untuk deteksi data baru: +- `get_tweet_count()` - Menghitung total tweet di database +- `get_latest_crawl_time()` - Mendapatkan waktu crawl terakhir + +**Fungsi:** Digunakan untuk cache invalidation yang akurat + +### 2. **Sentiment Analysis Page** (`page_modules/sentiment_page.py`) + +#### Perbaikan Cache: +- βœ… Cache sekarang menggunakan `get_tweet_count()` bukan `len(df)` +- βœ… Cache otomatis dihapus saat ada tweet baru +- βœ… Prediksi sentimen direfresh otomatis saat database berubah +- βœ… Session state `_last_total_tweets` melacak versi database + +#### Timestamp Dinamis: +- βœ… **Pie Chart** menampilkan waktu update: "Diupdate: DD/MM/YYYY HH:MM:SS" +- βœ… **Bar Chart** menampilkan waktu update + persentase sentimen +- βœ… **Trend Chart** menampilkan waktu update realtime +- βœ… Semua metric selalu menampilkan timestamp terkini + +#### Import Baru: +```python +from database import engine, get_tweet_count, get_latest_crawl_time +``` + +### 3. **Crawling Page** (`page_modules/crawling_page.py`) + +#### Metrics Update: +- βœ… `_render_metrics()` sekarang menampilkan timestamp: "πŸ”„ Data diperbarui: DD/MM/YYYY HH:MM:SS" +- βœ… Total tweet, hari, tanggal selalu diambil fresh dari database +- βœ… Setiap kali halaman dimuat, data diambil ulang + +### 4. **Preprocessing Page** (`page_modules/preprocessing_page.py`) + +#### Cache Improvement: +- βœ… Cache menggunakan `get_tweet_count()` untuk invalidation +- βœ… Session state `_pp_last_total_tweets` melacak versi database +- βœ… Old cache entries otomatis dihapus +- βœ… Preprocessing direfresh saat ada data baru + +### 5. **Main App** (`app.py`) + +#### Smart Refresh Interval: +- βœ… Auto-refresh interval dinamis: + - 30 detik jika ada data baru dalam 5 menit terakhir + - 60 detik jika tidak ada aktivitas crawler baru +- βœ… Sidebar menampilkan status crawler +- βœ… **Sidebar baru:** Widget "πŸ”„ Update Terbaru" menampilkan: + - Tanggal dan waktu update terakhir + - Format: DD/MM/YYYY HH:MM:SS WIB + +#### Auto-refresh Configuration: +```python +st_autorefresh( + interval=refresh_interval, # Dynamic berdasarkan aktivitas + key="auto_refresh_dashboard" +) +``` + +--- + +## πŸ“Š Data Flow Realtime + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Crawler Service β”‚ ← Data baru setiap N menit +β”‚ (python3 crawler) β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ SQLite Database β”‚ ← 429+ tweets dengan timestamp +β”‚ (data_sentimen...) β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β”Œβ”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β–Ό β–Ό β–Ό β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚Crawling β”‚ β”‚Preprocessβ”‚ β”‚Sentiment β”‚ β”‚Sidebar β”‚ +β”‚Page β”‚ β”‚ Page β”‚ β”‚ Page β”‚ β”‚ Info β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ β”‚ β”‚ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β–Ό + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ Smart Cache Busting β”‚ + β”‚ (get_tweet_count) β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β–Ό + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ Fresh Data Show β”‚ + β”‚ + Realtime Timestamp β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +--- + +## βš™οΈ Cara Kerja Cache Invalidation + +### Sebelumnya (Dummy/Stale): +``` +Cache Key = "sent_realtime_2026-05-09_2026-05-09_450" +β”œβ”€ Bergantung pada `len(df)` - tidak akurat +β”œβ”€ Jika ada tweet baru tidak terdeteksi +└─ Data lama tetap ditampilkan +``` + +### Sesudahnya (Realtime & Dynamic): +``` +Cache Key = "sent_realtime_2026-05-09_2026-05-09_429" +β”œβ”€ Bergantung pada total tweet di database +β”œβ”€ Saat tweet baru masuk (429 β†’ 430): +β”‚ └─ Cache key berubah otomatis +β”‚ └─ Prediksi sentimen refresh +β”‚ └─ Timestamp diupdate ke waktu terbaru +└─ Data selalu fresh βœ“ +``` + +--- + +## πŸ” Timestamp Ditampilkan Di + +1. βœ… **Ambil Data Twitter** + - Caption: "πŸ”„ Data diperbarui: DD/MM/YYYY HH:MM:SS" + +2. βœ… **Bersihkan Data** + - Tidak ada perubahan (inherited dari session state) + +3. βœ… **Analisis Sentimen** + - Pie Chart: "Diupdate: DD/MM/YYYY HH:MM:SS" + - Bar Chart: "Diupdate: DD/MM/YYYY HH:MM:SS" + persentase + - Trend Chart: "Diupdate: DD/MM/YYYY HH:MM:SS" + +4. βœ… **Sidebar** + - "πŸ”„ Update Terbaru" widget menampilkan waktu crawl terakhir + +--- + +## πŸ“ˆ Data yang Sekarang Realtime + +| Data | Sebelumnya | Sesudahnya | +|------|-----------|-----------| +| **Total Tweet** | Dummy | βœ… Fresh dari DB | +| **Tanggal Awal** | Hardcoded | βœ… Dinamis dari data | +| **Tanggal Akhir** | Hardcoded | βœ… Dinamis dari data | +| **Sentimen Positif** | Cached | βœ… Recomputed setiap data baru | +| **Sentimen Netral** | Cached | βœ… Recomputed setiap data baru | +| **Sentimen Negatif** | Cached | βœ… Recomputed setiap data baru | +| **Pie Chart** | Stale | βœ… Refresh + Timestamp | +| **Bar Chart** | Stale | βœ… Refresh + Timestamp + % | +| **Trend Chart** | Stale | βœ… Refresh + Timestamp | +| **Word Cloud** | Cached | βœ… Recomputed setiap data baru | +| **Waktu Update** | ❌ Tidak ada | βœ… Ditampilkan di sidebar | + +--- + +## πŸš€ Cara Menggunakan + +### 1. Jalankan Crawler (di terminal terpisah) +```bash +python3 crawler.py +``` + +### 2. Jalankan Dashboard +```bash +streamlit run app.py +``` + +### 3. Monitor Data +- Sidebar menampilkan status crawler dan waktu update terbaru +- Dashboard auto-refresh setiap 30-60 detik +- Saat ada data baru, prediksi sentimen otomatis direfresh +- Timestamp ditampilkan di setiap chart/metric + +--- + +## βœ… Verifikasi + +Untuk memastikan perbaikan bekerja: + +```bash +# 1. Check database functions +python3 -c "from database import get_tweet_count, get_latest_crawl_time; print('Count:', get_tweet_count()); print('Latest:', get_latest_crawl_time())" + +# 2. Run syntax check +python3 -m py_compile app.py page_modules/*.py + +# 3. Start dashboard +streamlit run app.py +``` + +--- + +## πŸ“ File yang Dimodifikasi + +1. βœ… `database.py` - Tambah helper functions +2. βœ… `app.py` - Smart refresh + sidebar info +3. βœ… `page_modules/sentiment_page.py` - Cache busting + timestamps +4. βœ… `page_modules/crawling_page.py` - Metrics timestamp +5. βœ… `page_modules/preprocessing_page.py` - Cache busting + +--- + +## 🎯 Hasil Akhir + +**SEBELUM:** +- Data dummy / stale +- Cache tidak pernah di-refresh +- Timestamp tidak ditampilkan +- Perlu manual refresh untuk melihat data baru + +**SESUDAHNYA:** +- βœ… Data selalu realtime fresh +- βœ… Cache auto-refresh saat ada data baru +- βœ… Timestamp ditampilkan di semua metric dan chart +- βœ… Auto-refresh setiap 30-60 detik +- βœ… Dashboard responsif terhadap perubahan data + +--- + +**Status: βœ… COMPLETED** +Semua fungsi telah diperbaiki untuk menggunakan data realtime yang dinamis sesuai fungsinya. diff --git a/app.py b/app.py new file mode 100644 index 0000000..a183269 --- /dev/null +++ b/app.py @@ -0,0 +1,982 @@ +from datetime import datetime, timezone +from html import escape +import os +import time + +import pandas as pd +import streamlit as st +from streamlit_autorefresh import st_autorefresh + +from database import init_db, get_latest_crawl_time +from timezone_utils import ( + INDONESIA_TIMEZONES, + browser_offset_to_choice, + browser_timezone_to_choice, + get_default_timezone, + get_timezone_label, + get_timezone_name, + parse_dt_with_source_tz, +) + +try: + from dotenv import load_dotenv + load_dotenv() +except Exception: + pass + +init_db() + +st.set_page_config( + page_title="Dashboard Sentimen Twitter", + layout="wide", + page_icon="πŸ“Š", + initial_sidebar_state="expanded" +) + +# Initialize timezone selection in session state +if "user_timezone" not in st.session_state: + st.session_state.user_timezone = get_default_timezone() + +if "follow_device_timezone" not in st.session_state: + st.session_state.follow_device_timezone = True + + +def _get_query_param(name): + try: + value = st.query_params.get(name) + except Exception: + value = None + + if isinstance(value, list): + return value[0] if value else None + + return value + + +def _get_context_timezone(): + try: + return st.context.timezone + except Exception: + return None + + +def _get_context_offset(): + try: + offset = st.context.timezone_offset + except Exception: + return None + + if offset is None: + return None + + try: + # st.context.timezone_offset follows JavaScript getTimezoneOffset: + # UTC+8 is -480. The app mapping uses UTC offset minutes: UTC+8 is 480. + return str(-int(offset)) + except (TypeError, ValueError): + return None + + +def _sync_timezone_from_browser(): + browser_timezone = _get_context_timezone() or _get_query_param("browser_tz") + browser_offset = _get_context_offset() or _get_query_param("browser_offset") + mapped_timezone = ( + browser_timezone_to_choice(browser_timezone) + or browser_offset_to_choice(browser_offset) + ) + + st.session_state.browser_timezone = browser_timezone + st.session_state.browser_offset = browser_offset + + if st.session_state.get("follow_device_timezone", True): + timezone_choice = mapped_timezone or get_default_timezone() + + if st.session_state.user_timezone != timezone_choice: + st.session_state.user_timezone = timezone_choice + + +_sync_timezone_from_browser() + +# Determine refresh interval berdasarkan aktivitas crawler +def get_refresh_interval(): + """Refresh lebih cepat jika ada data baru dari crawler""" + try: + latest_crawl = get_latest_crawl_time() + if latest_crawl: + timezone_choice = st.session_state.get("user_timezone", get_default_timezone()) + latest_time = parse_dt_with_source_tz( + [latest_crawl], + timezone_choice, + os.getenv("APP_TIMEZONE", "Asia/Makassar") + ).iloc[0] + now = pd.Timestamp.now(tz=get_timezone_name(timezone_choice)).tz_localize(None) + time_diff = (now - latest_time).total_seconds() + # Jika data baru < 5 menit, refresh setiap 30 detik, jika tidak setiap 60 detik + return 30000 if time_diff < 300 else 60000 + except Exception: + pass + return 60000 + +refresh_interval = get_refresh_interval() + +st_autorefresh( + interval=refresh_interval, + key="auto_refresh_dashboard" +) + + +def _check_crawler_alive(): + from datetime import datetime, timedelta, timezone + from crawler import NRT_INTERVAL_MINUTES, get_crawler_state + + threshold = timedelta(minutes=max((NRT_INTERVAL_MINUTES * 2) + 1, 3)) + + def parse_dt(value): + if not value: + return None + + try: + parsed = datetime.fromisoformat(value) + if parsed.tzinfo is None: + parsed = parsed.replace(tzinfo=timezone.utc) + return parsed.astimezone(timezone.utc) + except Exception: + return None + + state = get_crawler_state() + if not state: + return False + + updated_at = parse_dt(state.get("updated_at")) + heartbeat_at = parse_dt(state.get("heartbeat_at")) + now = datetime.now(timezone.utc) + + if ( + state.get("is_running", False) + and updated_at is not None + and now - updated_at <= threshold + ): + return True + + return ( + state.get("service_active", False) + and heartbeat_at is not None + and now - heartbeat_at <= threshold + ) + + +st.session_state._scheduler_started = _check_crawler_alive() + +try: + from crawler import NRT_INTERVAL_MINUTES + st.session_state._scheduler_interval = NRT_INTERVAL_MINUTES +except Exception: + st.session_state._scheduler_interval = 0 + + +st.markdown(""" + +""", unsafe_allow_html=True) + + +if "page" not in st.session_state: + st.session_state.page = "crawling" + +if st.session_state.get("current_page"): + st.session_state.page = st.session_state.current_page + st.session_state.scroll_to_top = True + del st.session_state["current_page"] + +if ( + st.session_state.get("scroll_to_top") + or st.session_state.get("_last_rendered_page") != st.session_state.page +): + st.session_state.scroll_to_top = False + st.session_state._last_rendered_page = st.session_state.page + + +with st.sidebar: + + st.markdown(""" +
+
πŸ“Š
+
+
SentimenX
+
Twitter Analytics
+
+
+ """, unsafe_allow_html=True) + + sched_ok = st.session_state.get("_scheduler_started", False) + sched_interval = st.session_state.get("_scheduler_interval", 0) + + sched_color = "#16a34a" if sched_ok else "#f59e0b" + + sched_label = ( + f"Aktif β€” diperbarui tiap {sched_interval} menit" + if sched_ok + else "Nonaktif β€” jalankan: python3 crawler.py" + ) + + sched_dot = "●" if sched_ok else "β—‹" + + st.markdown(f""" +
+
+ {sched_dot} + Crawler Service +
+
{sched_label}
+
+ """, unsafe_allow_html=True) + + # Tampilkan waktu update terakhir data + try: + latest_crawl = get_latest_crawl_time() + if latest_crawl: + crawl_dt = parse_dt_with_source_tz( + [latest_crawl], + st.session_state.user_timezone, + os.getenv("APP_TIMEZONE", "Asia/Makassar") + ).iloc[0] + tz_label = get_timezone_label(st.session_state.user_timezone) + update_date = crawl_dt.strftime("%d/%m/%Y") + update_time = f"{crawl_dt.strftime('%H:%M:%S')} {tz_label}" + st.markdown(f""" +
+
+ πŸ”„ Update Terbaru
+
+ {update_date}
{update_time}
+
+ """, unsafe_allow_html=True) + except Exception: + pass + + st.markdown(""" +
MENU UTAMA
+ """, unsafe_allow_html=True) + + current_page = st.session_state.get("page", "crawling") + + nav_items = [ + ("crawling", "πŸ”„ Ambil Data Twitter"), + ("preprocessing", "🧹 Bersihkan Data"), + ("sentiment", "πŸ“ˆ Analisis Sentimen"), + ] + + for page_key, label in nav_items: + is_active = current_page == page_key + + if st.button( + label, + width="stretch", + type="primary" if is_active else "secondary", + key=f"nav_{page_key}" + ): + st.session_state.page = page_key + st.session_state.scroll_to_top = True + st.rerun() + + st.markdown("
", unsafe_allow_html=True) + + # Timezone selector + st.markdown(""" +
⏰ ZONA WAKTU
+ """, unsafe_allow_html=True) + + follow_device = st.toggle( + "Ikuti timezone device", + value=st.session_state.get("follow_device_timezone", True), + key="follow_device_timezone_toggle", + help="Jika aktif, dashboard membaca zona waktu dari browser/device." + ) + + if follow_device != st.session_state.follow_device_timezone: + st.session_state.follow_device_timezone = follow_device + + if follow_device: + mapped_timezone = ( + browser_timezone_to_choice( + st.session_state.get("browser_timezone") + ) + or browser_offset_to_choice(st.session_state.get("browser_offset")) + ) + st.session_state.user_timezone = mapped_timezone or get_default_timezone() + + st.rerun() + + timezone_options = list(INDONESIA_TIMEZONES.keys()) + + if ( + not st.session_state.follow_device_timezone + and st.session_state.user_timezone not in timezone_options + ): + st.session_state.user_timezone = get_default_timezone() + + if st.session_state.follow_device_timezone: + raw_detected_tz = st.session_state.get("browser_timezone") + detected_offset = st.session_state.get("browser_offset") + detected_tz = raw_detected_tz or ( + "Timezone dari offset browser" if detected_offset else "Belum terdeteksi" + ) + mapped_timezone = ( + browser_timezone_to_choice(raw_detected_tz) + or browser_offset_to_choice(detected_offset) + ) + active_label = mapped_timezone or get_default_timezone() + st.session_state.user_timezone = active_label + try: + offset_minutes = int(detected_offset) + sign = "+" if offset_minutes >= 0 else "-" + abs_minutes = abs(offset_minutes) + offset_label = f"UTC{sign}{abs_minutes // 60:02d}:{abs_minutes % 60:02d}" + except (TypeError, ValueError): + offset_label = "offset belum terdeteksi" + st.caption(f"Device: {detected_tz} Β· {offset_label} Β· Aktif: {active_label}") + else: + selected_tz = st.selectbox( + "Pilih zona waktu Indonesia", + timezone_options, + index=timezone_options.index(st.session_state.user_timezone), + label_visibility="collapsed", + key="tz_selector" + ) + + if selected_tz != st.session_state.user_timezone: + st.session_state.user_timezone = selected_tz + st.rerun() + + active_tz_name = get_timezone_name(st.session_state.user_timezone) + active_now = pd.Timestamp.now(tz=active_tz_name) + active_clock = active_now.strftime("%d/%m/%Y %H:%M:%S") + st.markdown( + f""" +
+
+ Jam Aktif +
+
+ {active_clock} +
+
+ """, + unsafe_allow_html=True, + ) + + if ( + st.session_state.follow_device_timezone + and ( + browser_timezone_to_choice(st.session_state.get("browser_timezone")) + or browser_offset_to_choice(st.session_state.get("browser_offset")) + ) is None + and st.session_state.get("browser_timezone") + ): + st.caption("Timezone device belum valid dari browser, memakai default aplikasi.") + + if not st.session_state.follow_device_timezone and selected_tz != st.session_state.user_timezone: + st.session_state.user_timezone = selected_tz + st.rerun() + + st.markdown("
", unsafe_allow_html=True) + crawler_query = os.getenv( + "QUERY", + 'komdigi (ongkir OR "gratis ongkir" OR "free ongkir") OR "pembatasan gratis ongkir" OR "gratis ongkir dibatasi"' + ) + + query_terms = [ + term.strip().strip('"') + for term in crawler_query.split(" OR ") + if term.strip() + ] + + query_html = "
".join( + f"β€’ {escape(term)}" for term in query_terms + ) or f"β€’ {escape(crawler_query)}" + + st.markdown(f""" +
+
πŸ” Query Crawler Aktif
+
+ {query_html} +
+
+ """, unsafe_allow_html=True) + + st.markdown(""" +
+ Data bersumber dari X (Twitter)
via tweet-harvest +
+ """, unsafe_allow_html=True) + + +from page_modules import crawling_page, preprocessing_page, sentiment_page + +if st.session_state.page == "crawling": + crawling_page.show() + +elif st.session_state.page == "preprocessing": + preprocessing_page.show() + +elif st.session_state.page == "sentiment": + sentiment_page.show() diff --git a/backup_data_sentimen_komdigi.db b/backup_data_sentimen_komdigi.db new file mode 100644 index 0000000000000000000000000000000000000000..9109ac4401f6b223517912957adaee3b1d138a4f GIT binary patch literal 40960 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 F{XZ=YIo1FG literal 0 HcmV?d00001 diff --git a/crawler.py b/crawler.py new file mode 100644 index 0000000..17e3a74 --- /dev/null +++ b/crawler.py @@ -0,0 +1,137 @@ +import json +import os +import time +from datetime import datetime, timezone + +from scraper_worker import scrape_once + +AUTO_CRAWL_INTERVAL_HOURS = 0.083 +NRT_INTERVAL_MINUTES = 5 + +LOG_FILE = "auto_crawl_log.json" +STATE_FILE = "crawler_state.json" + + +def get_crawler_state(): + if not os.path.exists(STATE_FILE): + return {} + + try: + with open(STATE_FILE, "r") as f: + return json.load(f) + except Exception: + return {} + + +def get_auto_crawl_logs(limit=10): + if not os.path.exists(LOG_FILE): + return [] + try: + with open(LOG_FILE, "r") as f: + return json.load(f)[:limit] + except Exception: + return [] + + +def save_auto_crawl_log(status="success", total_saved=0, error=None): + logs = get_auto_crawl_logs(50) + + logs.insert(0, { + "timestamp": datetime.now(timezone.utc).isoformat(), + "status": status, + "total_saved": total_saved, + "error": error, + }) + + with open(LOG_FILE, "w") as f: + json.dump(logs[:50], f, indent=2) + + +def set_crawler_state(is_running=False, service_active=None): + previous_state = get_crawler_state() + now = datetime.now(timezone.utc).isoformat() + + if service_active is None: + service_active = previous_state.get("service_active", False) + + state = { + "is_running": bool(is_running), + "service_active": bool(service_active), + "updated_at": now, + "heartbeat_at": now if service_active else None, + "last_job_started_at": previous_state.get("last_job_started_at"), + "last_job_finished_at": previous_state.get("last_job_finished_at"), + } + + if is_running: + state["last_job_started_at"] = now + elif previous_state.get("is_running"): + state["last_job_finished_at"] = now + + with open(STATE_FILE, "w") as f: + json.dump(state, f, indent=2) + + +def auto_crawl_job(): + print("=" * 50) + print("CRAWLING MULAI:", datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + print("=" * 50) + + service_active = get_crawler_state().get("service_active", False) + set_crawler_state(True, service_active=service_active) + + try: + total = scrape_once( + limit=int(os.getenv("SCRAPE_LIMIT", "50")) + ) + + if total is None: + total = 0 + + save_auto_crawl_log( + status="success", + total_saved=total, + error=None + ) + + print(f"Selesai. {total} tweet baru tersimpan.") + + except Exception as e: + save_auto_crawl_log( + status="error", + total_saved=0, + error=str(e) + ) + + print("Error crawler:", e) + + finally: + set_crawler_state(False, service_active=service_active) + + +def main(): + print("Crawler realtime aktif.") + print(f"Interval: {NRT_INTERVAL_MINUTES} menit") + + set_crawler_state(False, service_active=True) + + try: + while True: + auto_crawl_job() + print(f"Menunggu {NRT_INTERVAL_MINUTES} menit...") + + wait_until = time.time() + (NRT_INTERVAL_MINUTES * 60) + + while time.time() < wait_until: + set_crawler_state(False, service_active=True) + time.sleep(min(30, max(1, wait_until - time.time()))) + + except KeyboardInterrupt: + print("Crawler dihentikan.") + + finally: + set_crawler_state(False, service_active=False) + + +if __name__ == "__main__": + main() diff --git a/data_sentimen_komdigi.db b/data_sentimen_komdigi.db new file mode 100644 index 0000000000000000000000000000000000000000..8a4a823d782478753411c0d610960c108f034910 GIT binary patch literal 172032 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ΞSFupU|z84i0Uv?wwHwI5m( z83wB9w}M{+_6x_rxIs7rZ&F)_^an);HL9USkuin6(5FSY?sr2Eiu7|=LW?3j%{!q* zk*?WtXiHHJ#_iC9BHedep+%AYvCYt;NIy{Nfv&j$PqU1iLNM5MN1~MYRA<*ucYSN( zQ(f=uc(Ci~u5`yU9rK;P*ZINGyE}fQ%aJ+4psQ2YpZUy{@mP_cwY!(EH`Rwcfej@t(i#`IVmc_k3~BcF%Or{oSAJ z{-y5k?S41ji^s`U_;^Pm@vD-mps`aFdO`^3N0J$XCJ2>_uFGi`f{JjCC>s_>1{Dgd z#nMHdB@^EfT2)w0!HUpdW$>_tc?9>e67SPj<)pos7$;bmux1o*Tmx3(yF;r`y)lPO zB(JD}@B{49LgKqZt7sWi$YBO4j~v$I8bp(jclIgiqqPkf)giqs$Y zJf4e!*k}2SC^-cy@vlRx&}-6Cs>DK!8xieTp@|<1t&%afX(4S1~l6&F|Ge4VwoIQCfE) ztsy`z5w}3%&-G7B=TW0zASwX@9UMg}pG*8%=ux7!fi6PqBuo^P+bWg#)6k<(+prff zJx~F}>cQm6CO#Q@6lxo1!^Ofyi-|8@iEQE%p+_;?9Os{EF<^-*A%X71pM)M|L0;mO zK@FB-Ei`^EC-KLjN2OM!wJ;jI=vp{OP|U2v{|G&bx!YuZ;Jw4YWG2Oz22v#cduSEH zhP{w>gM$c6=enWA#K$$Fi0*Lya{Wn|JQ!HeD5eUwOabo;>U`plLaWf-SR?L6fQ~te zl9sU&9}BJGadym5nz^PQ-gC)< znfOmSvoX0J=mG0V$d-UY5)CKuE1^e0KJ*Y*-VQDi6A=NEOME1>O8Iky`I*o(wZ>w+ zSO+JTbaEm-uG7fWz|0lGHX(i&8m3WId%Y@X-T;e~5R-qV( z`ya_EP640~Pb?&UR=;X;Ew~ySOd>u}IdHn6Y5h#-QNnCc7+`HUVemzf1dEgQr$eid zJ`w|xluq2R80hF*g~b0o-Zkgz@%SbH1Pch2(bxtI0@D)zK~oFRyG?whP!~`Vrzt$d z>eTKpx;gzzwGDCg_~CJ$K~#`2-U(c_IH3P84FX70wr!?F$448*ZUFjb;-^B7g8P|Z zn}kPTf1-?V--!=}R*5Ew(uCI)1q9gyf5WNz!O$wMPc$(U2eeK!QrHdr zc>@RumB_!)Jg&#YkB3&#(g&oHP!#GW@*1@nY4PtutDrBjc0@EGUFBKG#>9{5ml)Z> z|1SMWloN72=-RkBQG4K4sNlK8kA_y^w}X+gC*fHVbO zM4|wO4}@01BPDX3gV?zl6?w+&`yt8yA4~k}#ORNVd}{b3L+>A49oX;xkNq#{drR+T z&o_5}QP+6KxAXGfx3BhYyHm1Zw0_apo~sgm5BXo+Jc|{lqAXY-pbETK$XmHYehg_9 zfdNrud#V~zGKOch6lbko^UC4Yv{hKX#`v=F+76BN0*UV9ZYqkC1n;J4ko-fgt(1eu z6-^_h0@ZZSwizoKPRN#-Ybfg>Px4jCb+Ow9Q+c#Bwfq2+N}SPU(#G3MX_h=DRS6g9 zsi(Tn@PgJpiZ(PdQKnX<;K0dyo|p7zl@7>CvQSj>3e})2_%P{fK!Z}c&y$!*0VpN3 zT4Ku?<2vb>(#=XGeUr#2IaM?RbsEIej;QhgeQ&4~wLgIDD@N9+C(26ut!$T+7_zkF zPj=e2G-sD|ZzcksHUuC#?5V?#EufNExL0y{w9!ro?8mgUH3q6h_~%I&bo*tyXlLh1 zktbTGpCq|EIs{Z=*-mRudiRkpNp*@0q+-muq?xIdA6kUjFO&MS!FV|ibcw}sR$6b< z-pn)>=jQF-B{fdf$7~cg0hnQta;BP6o;GI7y9}S9vyl3b3roS|HoGbbusfq)04P18 z<#uU)x0fRMt0V=xWR!2y2A@tub9FvpFcRC-wr0PN)z-KNM1#y2#hUgzFn?Xn|guv92Id2P>@t2G56BW#Uz&#p$q zlo(9J58fPQ>5MXrWX7{s9AA758S+DD0LRv&bFK|;1t~mhJH@rdhSOcKK z`5J!zPstm#^ED+CoDxZed@9*IxO<96X)YDl%?PtEFoX5g&&lq4bW({7HL`n{@u4s^ zj}VyiqgqkRm^`ZQ z>N=Hk3#GR9sAG3ZoTvn|Cz3!IPG4fF=cblhv242>$`(eN47N+$>``DGRCvE=J`VZF zd-dZ|%oN-14`+Fxj_iqNIqprZ3jof=sA%kqE_T%V1AGQ+KHc5|j&ekLvsA&ycN$QG ziD*h}4~+CH%tBQ>&8N)unJZVXEn?&cs~ySOjqN0lHS1S99zAd%Fdfj4+>8ufFF2P8 zbf^eG5P~x&_#?lZq_`hUS%&M-E7ENcxmKn>6$9>^Hb{Z;(E!feT0aWX+}ab(9#QB` z>U_t-Ib+;r6i#~ixXa-*f%Dw;rS;P8x$|{#WM@t#Gp9sfwkHe5G6MXjUPkuOgEs_5 zcjz#RjNMPFlCu;?MlxtzL8h{(`p*q_jG#d$4S1E5dZ7|awYFE>uLf+WK`y-F770pu z7gr$N^=Vpx=1M(Dv4Wmq_uF#REgtXI>za?I&|sUia6GL#&vRb^z%#VQ>6FUFK%|s7 zi9V*oiB{9GU5f}+`MA`6tOcAx?B0w_UznT+e-;E?5MhBDYpH>vL z(WuBYEB7tjSwr|7dy*Q+3Ov#7J?v->jNx)yy!!_Xg4S+XJWc2z4Dr`V^hPZ=q*w16PDA8UINl%el7$e@pbtu$jNR72X{ z{0x(sukI}E25Yt%2*TLh*gjg6X9Z>~nC%g0Rd2{lwoODv@is!D(cC@spvZgIbZ-qB2ZNO1WH4VPsO z-K~k$%cZ>0Mm*NEL=aRq;6M3m%hUOowwzi(Q5do$_h zl82WIa<>zD|d^rRi}C;Yj}9B zyr;2kTpEz6jNYY>itP~8P$oCKQa-=f$m|fx^Gy!M;k>}$LXwjT9{X!&+XI60S{HH> z|Dy8e7aTuRq;Bszp}>Q6yzHQWYW{@oDBgRl(N^mn#wo6vM%^g1&MoUZ&*1ZkrB*Sn zJO|^&cE9ZWP5K>0=JemzTAJCTD^)-dD^_=Qc5L&>V>)OG!oMAtN}Qr|%V}agERr1N zi~as~pJ1FeteBjIsM-UULVR64qD=f&bJzB`xFR4xugX{8L`aqqToq`MIiC{($y18Md_Lt7@hhRbEDE4LvJ^D9WX!pv^YL&@k??qKu zxJvb>-Tc61+^UPDO?{qsHPX7HYaNm=u3G>Fje+4JbL}44{B<2I(cf+@{0Me4a4&V* zE=*ri{!z!};IK}(uFqDDS-Gb&V>QNG&mltR&gnOCW$meqx%BMpGn#TF?r%!1W13Kq zq8T{MJKEiXm1p(CMAoOcH(|#%%C;>XVsFu&85{(T>rcraJ zC6p#UA^8ZZ{%xm3kwyh#2s74Pt&~;OzOy(V2Z?`(Ggf#-Gg z(2A_R38~}uR5OB-a6AMmZ9(_>7byyF+!yln`n6S^Fy(0m_t2eKUT>}0^I@bXk8<)a z3G+458M+S<)3IzG72p7R11QHBx4ZqjM!eU(s#%7kp=L=WiI(oDv8s`kYG}ly7Nv;F zgkezwoXNs8DWGXE(Dl89D(*l}Ht7m@iZ31i%K0U$n7RDyVkk0`o13}u+}@S#HLoYB z*#ERv#vdJvmt@R(+*0iSJLv#0_Qp|jmAE z=Vni)s>lQk)h)+v$0xTC_Y+fc8?in~7tOA+g9jzJ*P@Cs%47pDk55-OIOEVMv zLUdkNh9^4nT5+965I<)E6HhK;%T24P(Mr0(4ONbTScvvO72zQ%wJSovrK*o0kYG}Y zhPYh_T-+WJYKnh2MVI^>l8eGIz z!7MjYC}wbFFiKc?$WJ+raz&NWU}sOeka54K+=cc(`{ zGLhx-lbd|tbA)4w{Dki@q@*v;Eu4}er*II@1rv%u(Cy8P&uk*$G!U}Aw|#$j-XQ6B z5e57HOmO!qMW+twMVdu*+PJ27W`i^tRumf^Azj~3yJ|nzzEAK=cS=(<6VWNaHQN+^ z+3pMFV!~gA4S?zIdFb4n6a7cF(Qw$W*0U=ZuLhXKPO6nEWpC)o(G%^L1Nu;WKNH!q z!rhRYhG|IJrDmf|%Uo60trf7(RA_a{_wlOSL;W;`r)mdRFIbHWDRF@j&Tgt%0k+m| zo9h8Lo9)fDdmuMh$f$9+v{*Yd>ORBpA0qy+76w%ClEiRZG@=tpV0#;jySuoiDy@Cy zjoH?PkhZktlIeUZ`$%Km0F|7H_{%4C+9SIQ?Rx~Dbq5BuZV??m0{HC0*Ka3Q^97ZL zm6lExf(^xBjXV~)sY=4%x< zAs#hbT>EyRgsbI-)dWY>MrB)9YIV4=4jMx5vCR-^6y$eaYg_HR<(joom=vW%HCFLK zvk0+!U?CV!M8wl|HPI1lS}I_(ld@X%E%FgU*okL6e%n&IMYl%InU;$WAZU zMcdh~t*sr>WwlZxCl6Jtjv4M!8XlMZ+WzLAICcqLFoAJ=^KjoF;9ZB1#+^5f%Z&9B z`PI!@g&$-f={R%o_)QI89}nhWj?1y#___P93oniTWe(1p8zrmKq`1`g`HJZrFM6mv z3ycPMWBV?-L*1*Bj_hdT(x=;Ql6>62&FKW{(XjITGDdn$;$@)TMj19nj;Ea`Esh-# zUit}0x{a4K)Bt$?c{~39m5H(J=;p|U;dc)`I{2=E|I~l2_pf`cp2P0%>-yu)-|rkt zeC21%^#61obabPg2CUEPuJq^{@g=6;D(8N!E`7f@YBD6aR8P6)tthH?2Tr@CF_xaZ z0UozUAQ4{-GQL@FRYYLyI(mAuku}QTY}Js8o$m(*4?`ZE!+i{9HChdwI-8}Rk|YWl_fbt zMqfxj)>&Y#$^qQbqV^F4P-mRfAxt0=7EFqMaL!(N~;}DD7Oo3Q!ZJL3gjZ&N7xZZ zH3i*vq|e$Z9GCTW|0K>+EaRt;ySSmT+$T#HXBVDJO>eI2n`b_Z8JF2d_n^S6-*fGx zY(8{}ku_@9@p2=!9qnDT8_A({wHtMpdakn4G)2I~UWL;3YP+>vjnX78^8)9fddpLt z2^T&p=Lqq*$levc`1jy?OYRWD!PDcKMJBF=j^J=7Th2W2K=20exbR=BTZoii-?cE? zJ}o=Hs`H4*ej1|Vqq(Cqek&1GnXWIj~3iQO!48IRg@o^kc`_KEiEgi7@gOD?imjmuSR7rV5RA(X)SFMkR<# z9JvMtMe~*2R&cLkBE}EKql7u*kzxCx_9L>_yEHP)M)p~n73jMgwhi+=*n_jJpr=XK zBLd+zeW#-E$3m56h8|Rpo@}2KhM_y0vypv!n0o?(kivpG=uB%;AwcGTl~B{LgE)%R zX11E>@fV7lf78mxcHU74g==e0GY7me6$S0WP}%JqrRA;m?gc%?I!+*Ls( z&MW#y!C@ja#wQ?IHUtz87}q4LT?WD97}zTAwJH+?Q36VesJ^Th(^2%Z>DB`tqtH|O z1CUMOYE9#9jR}xvq-CRruaC7)2oC6#N7=}ZT&|OPfzJJ^Owkj5wv7Y{szEKv(syyi z%M9i<{QBxOM=0)-YS;|Av5E5GvBQpvRZA#h_jIbGSF|4%v-s^g3P-hSQf9F&tNj3$ zQbz357<+5CY}@rO2N;X02awA#7FAW}iVWXS2U`JsrsS@a%uoSpQ8I0Rr-VX1T|u!T z0H*K?Dq6=YN`Sg(EZLTZtua-wnSz%~dNlLNW1|r?bMSMcF0~($%U#u>6WOB5J-9eo zJx$y50ErSV|5rX+FJ;&lcDULrO$t(xzII?U$gXr^37&_fOi5>mLsoi<`O4e*_JeYs zW&J$l7~zVYaYmW?|;a978}a7*mTE)mYKX3+Lu$=iFbXug*^` zGQHHFMtXggnWD2y8#NZDF7dwGj45>G2(rJmwGQYzW;B{!%;atpl0VZ4q965sBjVLoam4c$R%>`_Y`G^z#2FEH6uGYvsXm}8kY*l zVwrTY(zTMLd2HaoIpv0Er)_O2%Jso}14Z>06bd_I<*|5Cl0?Nib&;kSg70UU#f`F3 zG9F^y)?@ryUayJXwNX2)8CUlhEySXE+=VE5N+d6lJJ2MZ%~+aVo-PI)#nQT7WRML9CR#%GZ3f=)cpiWMb z2z@(2+hs4X9eaAUrvm5-5WdAA@-ViC0Dqhymr`0^>nyY%m#yoruzX~vA6GGv>rsh> z-i{oR?S|FpM1o%)(@9h_9JC<`t`s!K`x$6VqTnuLPvcZ(<`!6<@4jue-yoYmslz?8 z0nt*85pYUZ4yEg|zuh920s39U?Q$TQjK0rIEmj()2K4~M?a89CgPmxyRq#@hZ{FX2 zj7`5wAAOpS?1yvtl@4n*%h03D)Wqd3f{6;1Dx)hG#LMld z@6Hk^-x(U(5lc(kki}9ny{9~l@aBE(2`riL1x2-@I=-J5cRx?m zD^J!Ab`H|bTkj{%wMM~q!1lPOT_9cXKqvahCSbRk9s%OFxtDGdfoT5FHoSh=@W_I= zgdzI(1j@dK+IhC7TY34&&SNWLwYo$Rw9%m4+p-$s)EJ&|RwsRh3%*iMd5V{yAvZ-Ll8~wapsEgBmdh`Nm4-g-e)6s$*@L zCS{}K;qbKJ{Zp5QcQuwisrU0-P&WtLSx)u59)QnBHte2kN?B#CLf2U1BE^1o(8l(% z8;!MUQMK@*4wuj!2a!>ttWV*krdn#Rjt6_+Pz@n=%j;Owb|MV`S}*UTx&sch=Gs1@ z#n^A4w`{5QAzLalLSEfhtglpQNYow&wypcvy}cONypKDAoB&v?LI8OK55*tedhR)N z7BNNSGB6spd>KV4 zdXNvL8;$eM;BA0l)VIaTVCk;u+RyDbv@?QZXLNFmY>YlxY~s3L@AaxNSubx8&)vqd za&}L#M!kUJ{V62SH9}&8X(5&IM1nu6xz* zPlPWoP>)$g=1Yia7Ahq>C`!$r1LOR_rr?bxaxa)uPfeYhej=mUjs#?BSQ4j&%3wc_ zq>oHWh13|zz@=}9b7s8tlGp$VdDLK2o}=JZ8!f1dH1`%r zha3}D1-pa)lFF=@LC``bzF(76IF^VL3hP7OZASzg<;Ur;7M|hz^!nM&!)B&>NUK>V z(FRo)9~A-9I3tgy!M;+$oi9e*d1RB?SuH|sJF=&6J9vQcG7z13j@JZoSv(HgHX6XL z0_L4|=hn5Yr7N3d=&*hrMNP#Wx%5TRz` zlHr>6XlfE-6&QelRDmn6OmA+5tjIaM>;HwgkIfopOQmOW?uwCg&jy z3fNVQ!0vzIvMR4w->;g|`IQrW&}yO-j)}sKMou&VL^@-bD$T}|ChWv5kzS_nmBdX% zOBOuV3id>a)^4PHp}al!)aJ%nzeG>r7%0MZ}eIqAQ0LLbTcK1z;(JwZ)M{E+qNelV&LhRu$jz ziak?Tc#bVOiM}BNY^jQm%SjAkJ>ePrY2vlFJ!G<>NvHFm0Z;I%8UlTH0Ip-7A_$>n zwJ~%Zulgp4+|VliuvkS<`el1k@j`oE$TbAY$QI9{d3n(Y7sDLiwrVXJ$ZgiKOz|C} zRjpk$niC8E@5%r69?ENqF zFjEZ@VD4y?IA)`nfy0>*41Rx*$S!4p$hMs`4MNzxVeQs0;4>TunA%Cx&T~OVr6+s# zZge*ejZ|y(%gxIE%G9~N(4D|MW3?9Msd-L>qx;%({fW7-z>H}7JuNL!Rl=^VDhXcr z@fYQa0sV0@mu<6q2gkG0|Ls)a3Q2_xLSo`NBlO}$ULZYVdTMcYW<_(C*k1kH*akwq zvKu?{(;x;cy&iUG)=6hkYb+X#<$U{*M+j2u4c8xj+rVt#>N)!CkJUl96x zOAu#o)2ndKVlUjExJO{=1a-fhB^>?Nz5E3tv^DR?yyJ?iFzf{Vzm4FB)8rE*~ zuArGy6CRnM>TPy~|4oce+?gC(FShaZacVl7>MAD@6f`AT7`&+=nH$~WUF$aHFV|E~ zp66{lA*$OsiA#ssfUICS+5JW97yU7{+#nw0^r%0kcdhrbHpma+TA2v&aj0+)8KgDH zs((mJq`(8Md7+4-K$#drBT!A7ZL!G5oLjdwv=S4J28!QPr~dv_NTlW-M?lGA6*<%! zlZs5eO(s(P&A7Q$%1aK&SEV(n-fw6mFY35S!=Gk!+Yp{U3C&31NxO?rEW7#CL2gfZ zRclkb-CHj|o~{(n?sWGM4E<}pyL4Z1Gmi1uHHOWDp9G1zvxfb5Hk<_n3*0)t0!(n< zkJ;$dUKzW7>DHsTDV@XLP)JwG-;Giw&S}5UyDZsyw}1FE839HaW!c4Q7nk{rUN?pq6`}?dxkWywwh_FBggzqV@e4ch4oG!^v=&?wHM~dIl6cQ^9Fo##%-y}a0 zUE(dE5){}V>#os_Ty?-%CwoN0f?cDz;SoBSBS8Mcj`!{PR5l4=Q9Es}rv?p5q=*#% zpBd;3oZcD!rzdX@{rwZ482s$9|L*8-AH9C$Zyx@q@M!N9X8G|S{)-XHSvJ@op#%^& ziz}zS)m`Y$Zg-dlSsp=mr%FZO8d*O*@b9ZkfiG?`mZiQVf>&WJgzLOx2+@?=P;^R^ zV3*L9`Xk7H1xrt#uWqD4Z%uj?KkT%Miq2YTX6BTT+94m7h+7$<#1YQZ^H4REqGWpi zZN1Ow?Y$Hv#1B~f;K=(k__^p9q2VF74y6701>H8Ug>>DBk7`muatECL^{^7#NKZUhyJmPXlfd_|&&6 z<5%G_iKBTEvx*RM+!L|AMfL)$$InVbeKM(`z+TGYJZdw``UVxl-BpGtF0bM(nagP& zXmX}Wu_CE63W;R-lpnludvu0M=+uoX6XUnztVkdw{?h}QncK`HvXc1e-_!dHS#Gb> zi!68+&C*1a+w%tQq#mLa-m`2mjcBw}=J`rA9K|pzJ)8=yZO2<%;DIyvoyH>QYvqk@ z5SzP);Xm;Hw>kv0bZ%!oAbEvFjugN~o38d{80}C5j>JBxv%y3}2x4B@Hb)RR7I>ES1i4m(r3 z_w=M5Q3S&^S#(&TWJud{7UgQQ;H;)AY0|htv-net#m;_rD_KDhq#zv=c{(3%&V++- ztM{thXDem$P zvv=yCVxyldrYKp#nNvO5#Mcl4IhPcMW3xbDP8ErHD*;wmCD^V-@$Z^CcR4pQXUAJ*&q31q-h8rsnUU#Ag{0)grkO7Erz8WR4vKrTU+? zQHare+4gs>!AzE4U97)iuAp2DP5#EZEq9jIsQ>VLjBEh+hw|9qK@flo-uHJqF1)Y{^izrRc~8SlZKuHvM*xWhGM zTD~_Z3y>wB*W8zKS;V_y5z;;`FsS0RmFA@xq}I`Y)PbmmjaJ;D@hnOMOm0dj^wZ!% zm!l$F6&#Jy+g!DWN6d;UJ*aALu)W*_=XFH}EH3w={T9~gCSP1e=3VF_sg1#VmB&x4 zI^ouud$5#rJfl1=NY+T_9DJa6o8|vXs+N{Xv-rgFqc#JjIvFIyLyeN5LBnC8>j=8W zi2btSABIIDwOy7?jrCn{EO87}{RzBW=}C?}A0H)ZT`O!5>tttSweZ?!^%l7_h%+1P z4IKk0Aj|dI<9KQmRHW-m+glHIV*U#96H0vLPd>u(7f*T|oao)s0lDWLkWwba4@Truci*xc8oTc9V6_iA&0TZvldc2kMGvk+3Dg(@;BZ*D2Yr?spVRF#ZYE;|yM zjq_x4j?At6Nmh-NC`}O;iZ6pTU@_&kTg;$ry4<Sx*2DkX;eUMi zZyf&o;X8+4JbdENubuhDGymYsUpRCB%;hs5IQ@I4f9~`Tp8m6^=T3ja>35#`KTiD* zr~cDZ-+bz|Q`J+ahktYUr-uL9@WbJo!=D&Faq`zr{=~`eKKbC}#K{j2{lU;L4E_C~ zZy#D6x-|6u6Tf%j=T7|KiElaa`iYlLynXP0AN=nJ|MuV)2VWg54-Oyy_2WNz{Cke? z9iKY>@#9C2{m;k#;jzDbY~$F~V;?&De;@s4M}O$(w;f$N+CKW8Bfov*XO4VdVXW}M zfq#4Ki-SKl_+5iLPfJ=D$)ZP(2xpD)N5hK>D=dugj7yg*)xv|+RSn|yQU#((m=>$F zQ9zmAs1&wRSE-kg0un?O#)1nPGVrv8u6I=#C@z>wTTJZ1`wFKph*FTA77lp~ko0}c z3M!7FRR&AA7_!oOvvAm>xGa;|W3ZShHOP>;6C}St@4YAWal+n)N}WCl(huZcc=%eQ zaKyg~L_RW)Vy@&C5HZ6q4SQwbnEy~<)A2a08Pv(P6wnh_3rAD8qLK~T33jd9qv(B8Tc^_*f5L28(%A=XnEm0g~>}a=c1H{F@;@69(?jw=tk-8 zhc{Ymnn;ZJoFNY_`0S$PhRD}zHLz`)P(FiR*Mg$6S$JFOxyo>p!x&MfTNbJYn2ZsP za^bD1s{o7z28d^Zpb)`8ZlJ6#yd`xN&IV1+(0%fTY8fs;D_bg@@x`V-OF@DshKpK6 z#-Pr_diY-dPRw8;S|ehR#S>=HXnm@e3a3+7L2oPGZYXu(?*#yeZ7fv_r&3onVCQVr zw3_gyiUb?J+*)BcC0t@(7vnk-T7eE1rs7hqaMHU~1zHd|UU>ZyXAGc8_Nr2<6kbbR zCCq0?g!!SEQyV473J|V)sjCP_cp#XSp_UeBa)YOA6<$qU1qGV;bXf!#YavkKZ=A$e zQdcqV0ka!I3^z8c@FR4|BlLGuSFt_dmx+`c5(z`S7ny-L zx%46bP9W-YYRLW|EgVN=RGjFAnbcJ%Hx8ISiDl;)mc&0=)682&C$zX5p5%iFoT;`36qA7M7ENzfddOOx=o}Rer{#9J>PV zu&y5|7p78I0Se3H9WMZtG1?i`#Az(tNL|IqJ|}_ggNYf0$O`|IMhcUut2jL^y&Crr z<2Gr%Myp)7p1KP4CRN0lAeRe(NEH#P6ej%b;RD5GjXa}BOOO;JsMY8EJHbZ_d9SoL zDobaK-{4$a^A`&iSEPXpOJYmssA3DMh4Iv_Kq--;s!DZnBspwYm%`Q5Rm_`0uppSJ zkr9bAH|DA%W<L@$h`t^)Rf`=C5H zlw86l@}h;ysjFD65=_QTomw^sCgWPAR=AY9iWsg1XttQ9!4QMvTeQ`Mi>a%q@^cH= zzoE1N^Aaw1Hmfk|Zx0!Ev51rPs&XBI2$y@U=--K)E%eIbCFcu^!CSI&g?8#HA#%Y$ z4?Q&Im}@yC)l%U?>MH19IfqGd^t=1Sp{sjDiSL<9!J*(UE$hc3O;XcS)ZMR0`9 z9E^SmC8OyW{7KpBR_azb2b?RUYXs6^nJQLeq|i)VCDwa{fh%!*#AVJ2X%!m&_9)d4 z5gEsst^~v#%~~pb(7#iO?Wv;R>*8tWo~%1QK%t(xiqnJpqW}Z5D(8h$R4LR_S5aA) zsi;fM!_bK~;d2Ys)KzQ`Cy_>qDw++8i10U?g-Yrw{EQm=jPrw@WE~)?$ImFIt|IeT zuD}shWe2f9fS$6bg^|=%Y)_+!46>StGI@kDYPC>GU4`+%3L>nspQRR?h!bCUF?AKN zi_H<4M1GF*0!RstTn&VyszORAdXe;fxnPn!2jOV&Mw2D|J2*8RZk}g^#4J0`Hs0#WyOm z6u68?L%CM?aOx@*2AR&rd%!>;SYmar7rs7q71+=vOiryT^??(Cacq?eXT2jPZg5Uc zqb}hje86gOINtByi5Wd~9%>sIDDh*w4Vhry%V4=xuEc^h@zQx7{7=qC!8dlimRx2u zJfRP(fb8L)mJ7blhF+I%s=A3S?_{7@`@5Z@D{LV%9)MwW4P5%-jA zHASw5d#6nXauVkT1vpYjzahJ$0E`dOFg}taJ63pKY8}`eeqE7q2@TJYZj9i3r<`GC zELdx{g`bP%)O1&)kczaLcz48Md@4^yuu^B~2#?<8sjIABhFL<@5YRvY$RQaiyi@i6 zw+uWOI8!!1?IPu#j1_sxU-#hw09r=SJO|bvJv#`Sj|8ITuzS!%M{qcz~ zcovJlaZkZbz#yQ_*{Jrk3cn;>W@+Qwq)X*qhssM_Xi~bW3qQ1)8q|*E&}l6P1@UnDrX4YM)Bkk`Aj;E;B2So-_TiXUY+iO&E+^j+?WA5 z0V3r|6EDV`m*>e7@O89HnC4qFAz zMm+?MQ^;rzz_}^rB)gS@rq-Ntt=&?vahcsDdikQ3XA`O^IZVG0b1o#TcY6h6BpNuXr zEg0GD_2}0~2f_kRhE)XuZWrWK=NCzm7HRL<4R;}dr)Do)0jfQZq=)#i9*+=>^wxP{ ze~cG{JBuB`|7bO&l}Xy*ZZyJlbTq?B#4~PrgAUXSMoLLK+nHa$*Uz{eB#NQ-tFFfy@p>3 zC-x@T#93BgM`#Tk7rH!dIiomW6eQt`=}b~&vZ7nm$_`b1T)=1UtE`-GRH;;BguvT) zFAWK*02RsCqX+t5@~!CTVyZo_zXr!IF`{)8a+sB&r*V@&4o?zSnQ*iX z=!U8}i|WU2(pTsG=fyzsDUA5_`9 z8_l<0;nS#E0{UTXd{GOiHDU9ts*~!rAdUo6;B<_ceOY`+Un)D|X?bg`%cb5@<~XWD z_0RNH*n_@@N{_cPOBx6FuBE^LJx`9YJQ6b-Ovf;yY7o-$I%Op)3gfYH!oR*uV*ymf z+q*l(PdNh!`ahF}*a5hYLNd+hm<8+4q(9wZ_NVm~P|EG9h``6VdY1&U%eqpAq!bwc z)fQNQDyb!*={XnJys02I^y_+W=`B;Z^{{n_QD$+%3B7Us@l0^~;Bc)?1dUU6veZ^Y z*A9Ccwp`K!os=v8s8r~M$yL53oT=!Uwt7q2rjL5ohQtk_6k!{Vm2y*x^-A^dp@M0e zXmJx3!h5VDwIWrNz`C)P7!i`Pz`zD>7H{}4xY7dA{Z#G;zu2qgiz6H*Vd~;4Rm#oe zJJY1Z%xC^Pfqb5qGN@@Lp4@=dJ;>mR;=e!vk>jes}HY&=eIM9 zTgKsRuY%w(%lLD#-Mx(=^#{-<-V0h+P0s$4L?fV6eyNDwF9=8to>nS<&si zFV##)cgMTqnjOa!njf0jbWR>qA(cG0iri*1I(+@3y?L#`Eq?_dS|OSvT|+3w0*sJL zYcy;k;<<2UHWd4?-tD^_PWfI8I*9@XQddo;U)MlzFqeCH(l@ue_)D9(^faoj7GLhH zp?^nd=$3Nv%FNSCuhi?0M;30~^uK?5s=2iA#+8Zfm=h6Epm&p|^W0%u;=ibihyIK? z79!Q3K@m*_ACgj>dctn9G93LU0+7&uiTBfr@%V!ZPZrR=Xw&N}j05sc{1aZ!49W$f z7k1H%rRf9nVD#>5jMiqxEoP~#bw~PRQLwNy-3SIBKa~z($9!fzV`47?I)_pF^{x=O zQQiX4LWYDe%ZKb@+qSHVY~hl{J5Jk6F!dPHwR{JQ=-z|B+fAuN3l-3IDNwwc|5k`IE&W3CFN)x?1HcMPRZR$r+%%~xHtDkayuNyPu~0m z^{rCmrYQbDT^JrXGj{5iPt}G$fAZu|^TdY+j~v@P`sa>(_+f^x!YVW+;PJaeu5^5~_pi&Ik*MR3H%ixXovMsJQ5r!U=}f^mI(dR(Y~0n*>} z*yNqj;^?(2#aE{8g@a|q%cHLpM_(DU|6jjToE*DxSqOp`$8L{;C^%g-kig5Mx5sY+ zoW`i(%osQId)j?z7@g`g8=cG7UX8m!2t{ek#~aEs5)|>`)|}qEdtbmq`@Cl(pz|TS z31=6%Tii4}G@2l4ZP2VyGYk?4jLca#*N@NqnH1kMNX6~pXspYnqidpvBe$jhsP}o= zmcQKapAM)E+($}(L^ima)Owq(cIOdFZLQem$T7kJfp+q*cqLJnr1bTIn0lES345w0 z65cSc7J9>KnhG-@&j+vk0;Vi5`x<4p!EFUW2IQtR4S`dtqb#G7ENlgKiH3}c{u{az-U$I@FFe>gQc8$c5mawI#Wh-|yo|0!Hb{<~uJ(kTp?kznuK6neX zf>7I&!>DMc9(lrb)osbx{-EkdUMx1u5DBV#&%{&gE$3Bces*CmH=>_m@Xyq{MDN`U+}g5T(vXx@bTrw z%uGu1Aaux^2T{E$@&UlEek95B6Vk94S?2?t8#Gp^=1tD^tK5`mRKbB7J*f;s%aNfJ z!v@-1fP>V|rhxRXFFxF!SgXEvZQ5U*D#L1d2u}!OjPS$=-$Qu?DVe2*0;8i^znYK8 zV3Vz{j~cXzHa;{kmOxt#v7M(}Hl3YLB<3ezecUMCq@R9QgMG?Ik?*%5Vdfe)19F^6 zlG<=$0qjjfJ*bAv!}-|jaU$RI5<73awzRRoH+^?1Xu)pNCeAGc*y^u}`bP7nvQ#*a zXPu^cd&o@BN5!aCXGz{r1Efnw10LiYOXYBnUn<^S3>w*0TOebdH&-1%l4UzJ zQ}-SPwQ7T!pvtJ!&Pt6*j17QMQn@qOEl8(y)-`V(_QuAn%re^(}i6z{TY$q%AE z2cLj6L{3D~CSqOoaiZoFcKCu>Ur!|z(6s4GNE^%(SCzT4j(k6<- z{m>&D_LCKQLJ0a+&LF(pwT6yn1g( zBILow(mXWVA_A<-6sT@X3AJ|7ZJ$JRxNnhT z(SaFDvp4KdAWLr2z;4WM(E{&oiPpEI1es}ai-<6O{A$@HHTVFU@%Mz^!8aGq7w>R1 zNVWnkc%q<_nM56FuRSI_hy+a*l1sA8|xizdZX^p&*A3Ea_b8d~^ zmgK>gWSTSSWUJHILKUA@7*+2ZHHk3C*cak(ohHZ)Z=er#i{bYY=?8HwGe*f{`K7RV>&mQ^I z;nhP2g?WC=SHIRD<|Pk2fOC2#vYL9M0z@eCu4>_r_?gSS75hkDawo)cvR{+}(~=-~ z0@E~*N;e0gGo#daU{~e@;ec`s#whJ~n3vRo+l_Rvf~iXE3@30+JRur{lN{@EQ#IAh+|KZ%|@@DPvbugmCjo*;aopTWJR5Tp&nVxgm| zjV4_zZLIS}qK=2P{1c{Nxy-bWA=y}AAn zg4I9i+1+|(H$Qo1vYW0VsD}*Y>nRz+V2(|Z`y=*Spb<>T7T30{+9G5yv1*`S=77{- zrfO?WGj2HRhA|)-0in7OQ~+@bOx)-z{1F4A=&Z#F)ipi04x#`pVl+yXF`)du5)B&F&O`IY7;FKl;>Ph zA(T0RvuKgcsNP)Mo+Ltcek79U((N0YGQCeS+9*dNR9=gM&5v+aSRY7z0u-N-GU{sM zMf?RllOden-`lXBFEfBT$=z)MciktmC;g{26>JRJ4Gq^tzxk?2pqzP84Xo=41c$l) zu?$s|ITh7jWXJ_=O5he_Ev~JTfS|BNrN%#M3#%)5>~d6*0yVbq4L3Cij)ql@+$zf+ z3fzTb9Vl^^fB}sOhiMMC#W|6wV_SijJ1@{_siGUXuOt1Va=bt8E8MK9t~QxWR1sVi zHyIPhBsvtmp!O?s8h|<=*V-ljrOjWo5`OU1{?^o+Fg!QIJzC)GUXdr@gUG1J-ye$i@lB+BKp%2=SmPO~cOS$g}<0x42;vv-oA`9Mc^$$twcq|%EnQ*iWDhT7T?Uo&vm#;Py zn-b@U(AG&=UGO2&4Z=d1--C>oAK>MO+!+`X$59rL>sp(SuGI!}tuKsxVw^@l?^G5! zUUtmI%_X7>9>ZD#fdb=k_-TOBsGbCxr1TxD+VX@1NrsGKPwH(SZFD%{`HM%VP;?q! z>lawOUaIH_bZL@` z<$UoPo4_~MyUaI}D@HGE<4Uf?WU&||)af%YahP?A4bs7>{s3?HWgm{HM*lrCK+HQZ zKSApORa1haMA}S7-=$2Q&k(g>a{N{>G6qw}i+X%hPa^-B{~9G)sV*qh#v7V(4+B7B zzOyJZ7_E2d158^GPGaHg)K5IC7L=96aog#ANqY30uP^XJ$pg@APs)PP*%8Kj zt4$JfVaJs#@agnL+9MPw1EP~Kk+1@J!cMKjsPwB5nWxg?t>upV27Xd5Bx-`@M8Uc2kRHm)jDs5S_rh7a<-nza7TYP2Il8~(a1{%mSSTxBIs^>Js@bC;rh7V6n`z{_n71L%9*|bM@aswD#$BOGPoP>NPoKjPU(>iPmeS*2bw1E7;OqfIl^^2xLYft@DYqB zGG#H9I;j>cQGky@hk`{hyfE*$j2Lk&)Iz2L92sogP^O7~UFRJEg$sv*i$>Q$4-Ew| z9xr-YPSctUY`3|39vp+`;?x$!?E5%qK9+3+Arw>ZbhlpPsp z>0zV*A){3VLR1(HHb`rzBSVQtPM<|K23sFW+x{-;1JVH=d$^pFi{sOy#p`1i$FCM| zj@_71Q(43^`||Cvu>cU2v5Kp7m(hn7w3u=6mC;v5r*THEj$S{<|BDl&Q5TzYTkv3} zuiTloo3;s-f;IOSPW`w!rb6%rBT-hg|Hb~>SU8`&^C^faZQ*2+>Dd)?=HjDbWwQ9w z-(75$gs{A|KyKnw)V5}5yXaE=VbPYhsWx3uen`Omb0hPJNE@bYr9 zkZ-s&HUY?8=#t5Shzfen@CWx7!tZwHmiLwUGOjAVjUb887Wa1!%|ZCIt)Nyaca;RQ zwY4jR4COTJ(aG|P^$x1?s`*Iz0)>L)8mymMe<|Cd0${zcSdu+OERkpu_xjOl%Z{~;dB3WD#FD2PVqr+(P@N4JS9{dJ?^vYNkc z%sq#qaqzbO868f)^QDmqF4RUUDc>$EJ}kDUHdik%K4P#k=@GRjJCs`%Mi*A;t*L_U zop}{Ocb@ALcrX5%yCwzKqkuVHR5zva7%z!l=n^V58Y}}Z({Qpe?T#MKS~IW@Wa-Zr z%lvYXpmw?GB@Ql%gk3aqf4*2DVDCaMFCBuDK%Wl#M$(GEQdnRzZGtupLCM)iPq}8F zO{4b-ql@4dQ@et9o!3Cmh)RNF@s0jz9az5&HXEoZv=%Blr%KL=2Be_VChV8E7`$81 zgEZOM4@Fj8kzr^xpQvRfY)RM_j;(oWbor zx62=)v-na_-B0N_!EtVMm+_!Bq*0|2T|!wkY$?huygt6GR%5wgqGzFcnT-EcoYn|H zo)<_$F*3j|{{Ps(nNOVl^r`=G`1en)ocKF~?>zq8v3p1V&XIQ>9x42!tNsrRpbegX zQX8}~ku=&%i`(1#)WT2!8;g6p`<+=gLGSWi%I>N2h+3ddyAr^mr1CA|P>QuUKV|$L z5s;x&i*`fUhcK}GiouUsIngC(mu!|uF$&k({CkkNH3CX&q~ykXJ4Cmxj{-$XWVWc9 zss)ZB0RUJvoU@nf72w$hyRX?c&b_;p-4$Iyv%y6x9GjM3QQM}rZk@HWzQu_*ZPt zZS5?C{#)^^yvWtgRe>Blw(W@M$cp#$+5}Sp*sJYY7|FUI$&{ceP8495Tn)b0f&vB< ziSi*k*adE2kh{7W`)3Eo8G93i_~TrQ1Bn+o#mK>8C>c_&E<5kc@Zz~?T4ho$yu2+o zipgEhoHj!Np5d2E+~P;^7re|ALe7C@Sr)6i!KxuUGZJL01N0E5@J`2qb@DZOW4w6^*P7&4f*S-m&_;f1&{_1 zsqiJq+J9J5)7oYBw)2u`Nc3&EM>spidZKxs%~UBk8= za-Iweug#O_t*0eo#VN>0IScA(8XD%UG=e~QfnO?nIHkh(wkRG^-W*?6Vo=GM~ur8 zgR&Ss@#X$mJ@J(P#F@}V?a77x*-?StcBl%C6t8Si+BY->9UM$x%Hex)Zr$M*yL1lT z9=*8ii8xTgH;^VEyq8nF27XZNa*%q$<;Zb@K{iafTMzml(s_Tudyj13r0}y*cB&4F zWrC@FrLPep%$mXmiwZ0dqF>PDHlzTsh)!4$IvcSjh?pYi8k=UiH!wrnq2i+qK5K05&{OpcF_9!HKG;|!9fTl5 z8wLjELBzVjDTN(on5ZcW-rR)>3G{R2U!@G3hESR?s4%7i15*R34IvcE$&)Z^m>60< z_*B2hhJ0nzm%>cQt@h~l{_IU+zlp_iu}z=BP?tS5Ct$TxH$?ohRP&QVB11k z2aEiW*?c}cyhM2#*P*t9KDM7^#5L7gZ+ zmd-{)%R4pg&dNq-fx=&Rb(10qAOxu+XUB@8TfBhRY~{h@f;{V%u9rF);r#RKNGw@@oqL$UK?d# zv+2R1iwgTL9nLJ}{9?>%PWGOa<)7Tp1RT}a1DNc;50OtXQ8hCG`ND-W3mIYu1A(Q31{v%We zqqV)+f3IH1Z{clbVh5&Onn}|nann<@*q&KfS?96hMJlJdekVOVl9DioCg5)I$U#sBqz(?5IapAGL0{o4}@gFkw_ee}OP`p(1q zhyL!N;enqWs6Sc$@4cg6N6NfjJb*H@@LJnAML|N<+ho_ox|@X&`wE{{bWMCXQy7CKQC1;0DlhZY<_NzO5b(L~O#&HJ zrmf>8_>S3$&?952Ws_m9AU*3Cbj_#Wq^&S1w!Q`V32v@PfB5jg+tU?pIb8cfLM`B* zWRJ9tZZB??i^LrOL2)1XGL+tkwVqhKyiPQ_2*G9z7q9pJepT{aNIDtd)n`HH#*kHa zZr@SSXku#e@;Fckqm!e4FHBJ_G#AINjgQ~9uhauFikYX}1Y#Q>z^na=9?;Wn^oVAm z_Kvzy%uwbsGsk||Xyv#dYIKG8po@Yt=&Ao(_wH?7T^#LR_Yybw*Pw{xX-C?O;#B6r zyZdF{<;(m2VrOCtLu>bVZGU!c5AW(JiSu^nG002nn>)L3qh_YoF9|GDsF&L-_(n<` z${VsggFM)$`2;T&C+VS~)?|ZP@CV^oTJ4!TMc(f7Zr;lIV_s7sjuv$|8fEs#=xx7H zyiJdH={z(7dJx=y4ZSiFCPxw-N52HhZ!JpUbM1$V&+B*eD=*;kQ(-Ky z{n&m^10()q#P#b7S`9(VDT)abxgMzzPQpmWNyvnBPGr@>sYs$rs9by=M!!Xg)7Ij<*Mu~~FLgQxO!r0q>E_;~w{ zJ&BrwL^Nf|Mn-tdi4Q)wvqSrYoO<_=++dGf<(hj1s=g#Sc^MZuM3a9WmKsPvHQFh2 z4GshGQKa18?Z2pv@M??T7i0l;Qys!XzP?9Rg40A}T8M(SLofuZ(45kwHXhI=HtKG% zocQov_?Rs19Ey)6!7p&7_qj4hQFrHam6w`$K>T;4e5E}5Pfibg!Cy^TO;;GNA)#lPv z`N1s)WfEzbvhhaeuY0K;`?=5e&+CM~%Rc~_5a?4bd<=8L!QLMYXzvdl|0r z`riDW|K&fcuu^k+Bi{PMgwwDl_^-l*d+dm+oM5DNhxu@P@3ulZB&`(=mOW{7^uvcFq@|2P2EO%k{}p zBRTl0#v-~Y^Lz=YH4;;qkNTg`ruZ|v*?{%oS#jztnX1kQ>>%|>9?%;!36|JSDRvKQ zx}`JEmAQx{=Zh|poXB!VIugFIu-vrKU7;<^3JWo+sVW8v&^NlNXE9Z4Du+B!0D0=L zQX;P@;UHM*QTp3*X6KUU!7Y8x*dX0}iCjqkbN!EN=?^E*1AG}-EC31XiYReB@h7@3 zhR4+OBAqa3PoYHy2_a7MvXw|Bd9uZ1kQt|v$L%_1%+tHgD7tzsP+JCqN^PHvr7H?Y z293lkt0q8YTJ~1iO4*jhVwL-cGtL)^lgO!sWHt5q?`_W8=#U$E)&?tIMHzE<%T3lJ z4~z)v5yv?(jC9R2pi0blJm^Ri>-5C2t zA_hOg2VP0vvSTvGD8xDaVYR%6WU7PSo>fXkG=!>Ryt@r3swPX(y3Ri0<8orG{fQ7> ze#Z_-ip9;Y$T@*(r1Jm<`mjp~HEE50p>(^sq$oJ{6iL|avrY!bi}_eE`u}q!McfAA zf+_wVF8uj{)0@LTJ3Kh_{U?55@cH9kIQE-I|K5@R>hK6Z_}bSW)e0PZsDBaJ{fajS z53x-aDN=ifXpg1}nbtTIZwnf9%T-?7M8)`J^pwoFs^9$kIoEi?LGz?3ES8321J9=d z14F*DitP)*bVkswjITh3Z37m9C}y+XCw@On>^c)ky=t9MQl2I4Sza}Td)T#M^biAe ztGB}XvaW~KTJ!7qG;25DxKM+Yy>nXIJYr|)tl`@lwyshD)G6aA17f`1*x3+mjV z71U=?FU(KX^6G(Lr8lA+#KBO1RL1MmN#g~~Q5Gqc=k~t(Ms@SygVL+3m(|JO+#((w zQ=8=&#Q=ly2WbLUfc~UUWjU%h=S1m~g0i|rK-U-z*)2XlQh%}Lb~bFfuxMn9`hwGZ za+?Kgg!WPiE%c(L_OpOYGd!6vjpBQ8`z!bFuU#K|<>fem&u%bV_^dcs#CxIo>aF$L zGIa0vb|ahED?TT8RCgsJ;uU0_0}bnhBnaFCCVYU*+pqU8$h=kj*Ue-oi(mI5 z!bVg^Qxay2vl6zS$Zf|pig5zNtT|>YAjUvIxgta@UbD=*mW?aI^Bs(@2bH#mJ3=nP z*IJ}%w!2p*sN8i3Dyffh`BhQ$y3J6}LgWcVHZOFCEMpQUOiWN{N67Vvz-*6KuJ@My zHQa+leI+MF8sDWHa5U^Gu4vK{L`TnQa%~T9KlF1RLjLr7m z#_D5LY$SbhMAL;E@4?)Vfo&Fkn<}0$fO_2k)B&-{3ztN-6%}(1&h@{6XZ?!jSi!)O z#Z;F}ICVBQHo9FrV(ip114G@l&H@dgiDV^j5!46hA%6202NQ#L$8Q#Q{OVb8rn8B> zT*S@BNnKrD*#$dg6PcU`L}!(`2{bACH!3!#=c^vAtO6ivcq#N9isP9nuPt)*L=lRj zXyfZ*FlliE4uK>ll^Jfz_vrC|$Wa57WT_(@BIwwabtzu$KH>)=iRMuW4yaniyWan#gv*=0Wbz`5G;oyE zEFxv?{ejHClv-2V{;_ir1kL53&5qC~h+9)V#Gd7=j6IsWbhoj*Fy+6G=JN9T?tmDG zX@UBm?!P3}=Ydp$*PlgiIN9ke73Y?Bcj>L6*1rYD7y17p2GqRwz|}{wT^VIyX6(|P zX);+?ZN`Psi`!F^Q>hZh%$=*ni+3g{b6gz1%p`DB?cfH%Z=Qedx#x=au4t-;+g%>N z$jIxW`Rqf^Uz`jPh50eM}$DXz_J^iE$i$a+PTR3RM$+o0}IW zMz3o`SU0+HW$gAfeV-4~vj-DC_T+qia(OizojW4|Zz-4pkuJK_ZwV8`YuBQ(mca`t zvk|HuW?Sg5#D>u0smRzdT*#;fJriWvXgou>iP0S*QEYz_c}7ojMXy2485lMqnq2P; z0D&kic<|nS6Dj#}N=h=BE8A~sBH_(l<+@E3;8D>8@t9Xr?m^%nTYJ*MG|A(IQr;%d zsID&74F?M~v`?-aH`mfI{JAgakY^~U%vzSikPg}^vb&os>EwIYD4ZupA$b+6pAg@$88DPy>{q3!Yd_bVfrl%ehGw2@szRgRK%NTh)he+^xSrwLN= z$v7t9EqWnzXW4H?yv8y( zF2I_it&6daouOlI8@M^}tA~zN&ivw;e{kk6p1FVK@|h2u{=L&bclrlUf6M9DPrr2f z9jE@ysef|nKRNZKQ?H$>ojNo8FNc46_%xCrcwCq{`mc`uH%I^a(Z@$; zj-DT!Iy!jdSC9Pok?%UPbz}m1`9C=P3x|Jr@Z*K^hrjLc(&6^u_Z<4|Lq7xH$*&&z zzQN>nNb>&L!u z?B4J@PyW`)pE>yhC%^UN{K*R^-#hfXLq9+CLqp#-v^q34^q~{Kf8rNU{K$##Jh6G= z+KG>xI5hYxgFiO--7awVco1Q}f>sSY6|v94b^}`jv=fcOcciWY)evq|_%q>Cg`AEe zIMla=zmU3$W>#p6Kz4+`(1?ZszHJu1)xQe*cXFE|28F%43~e1`D|Ah27LuAmWqh2C!BX-C{z8lf7Kn@jehV zXZS(k1{Kw^D3@v8<)VLo+q)O!-Ac;B9Rvp&^+y~Gs8wr)zmmEa6ba%D7XdGUhRWg##}>|scS zWE8q16aa)r@C<(?bu+}C(*Rtl;rcU)4e@HV@a5E1WK)?Sg2S-JlqL8*VgRTW4m_EF zvXIBubFm7LoPs|FDsG{lx)l|5h+D-%F3M?Mg0t2v^io%`sv=eq(RGUwj&>ZT*KbZ; z#i|;+Dg0_QwTwuh;Zgrm>MB;14@0cLsm6WeUxjb- zuL9A6GeMBV!R9oA^9n?Ix$uS5Rq(S?{t&l&jsL@~g#suSKA*aZ+&prCHi2vqRMk4a z(r6VPr>-J41RWU)4wej7Z=jLI^}3(B3W?_NIkNP|@R?w@Lu6QZBXt!sLed||J(2~r zBWwhXk-{VN=5wKS;8=4J+!~yIhhz%8ABTOLWe^rov|-kkI~NaEyqU&HaRbO&RJnahC(T#53d23X$T7M*Wfxdsz$ku z)dzoE_M-V&{~Ctf=@N#&2&Vy!z`L`vpYg9LVF-Cv9Zaw0&l`L{uOI8vNEJQ-XOOlrMy&%cd|l^i@_XV+1$ z_+WhWtNv~3ME?-AijNyiYe*z9rmy(7VLlc0C*D((0q2-ctF-R=*HjTOSWzLKhLKz> z!~9Xb<6lFfLHyddO0Yj+{$T+C>)5}BV+^$?MBOG^HKZ0&_-X%|5!^0d*I^+SmpIxN z!(P7aUxNczhK?NJf?kIO0 ztscvzok0@NONiD;t?*smRjei=0}m=TR7+Q8$8ettzAsAR)HuR9z*RA=vIX!>;;a>MsSK&yb1~DmYw(f`$#@$}^LqY6y@L5;?|G>cM-Qj;u^gS{3zQJ!f{^;0G9R2PO>I@62S?61@ce4N#UvZ$+_VxZG}D>{ zux1k#=YZj3mYo(H`i3=e{8K-yoFTm=b88x@BEe*rlCfUfvwR}$P3kLAWxqJLxeTW> zQ_6Nz{UV7ZP_zMoA-e_6fsM!Ns$|EJCT++-1_ic>sY|1y6B84~v;0b!+&!E8i~6p@ zpX%>Xl9pOr<)vyqlygh&QdGq}_`3e2vVf1gDkk*ggw?n~SSHRQt_qINh*v7g>}$W5 zENMP96G*l!(L0xw7o=Mlv(Z8;}dEoC2?qjQlu|(3vf`qe)YHQ!^u50>mdzYA+;a|ihtmHlQC#1HY+Qs0* z9LAljUU9%6{|$u+>_U|uc;B!T9RO>F2_g~82awUfc4Mq~?ITn(J~>mIUcI!a26gSr#XFNXqKj@)92_U1ya{{@V(9?v zsRYobLiHm7#>An4zB(UbjzdFsD2#&oZ|P64F@10Nj58u%fJhFsZJZ|(Nw{j2_5^1h z1+m*Da5n&GJ9DU(pqy-d4$LQkpTYZ~PgsTBm4%>+MF}(Jm>0Z5mjWy}{?GTW+6XnQt#>(M4U|?}%V?}%& zbt_GqoYp}72Cq*?!lX62q&~6lgH|cChJ$dt`Jk&MvV7S}SZl3IqQ*P_~ z)!8y(5t%vMR%MHbS8xid*C`g!F%|EQc18N711<|d<$^qjS67ECQ0nIyH3*^5HBV>* z8ya$sCS;N-QN1cwkDy3JKjYlqzKJ&_?NzY$XrJAofF~4{&Ar_{is95NG)HY@)J_8Y z<$yq`l-GrbY$rP8-`2k>t>fF-GTtIr;nd)B*2TPwAKT2)I+^w%U7Cw}d#TpRSgKPK z(jAV6+FPj3SuOmjhU8hMLn5F?Ra|B$0s$D27`aJu zA}xQp!n(QjUojFVCL#ngdItlr3vh5;({-wSQzdz?JBrf(Gps&AU)%-UyW1brqx%EB zkg~JUWHxaS2y7`cU|~u=;;0%h>7#HSi5s+`&C+kMQF}@8rVSt@dPpt_nmVs-q;q}g z<(FF*?<_1OWf%+^e9b%=h8rV+Wb&5&WoZ`QY(mz~>nKr58kyH3A&8;)^vGw!HwoP0 z(6_qa9cqxZRoJz`GvpihWW)MOjJzWK@@}S%$;A+?VKtxe#tBdfHB&YjTwGu&FURG5Hv^Zv&_*EJ?T7-CN zF=_=c?ZY)#g@7{37}a7dGuBuhc{mj5D0>wOgw2%+1CmCf%4Mxvm%1WY@0R^DEH%%+4Zk3&0JlRt;QfWZyEs87RO& zZP;{YZ2in0M}0Sm!f0}*y%Der2sgop!vWir3lQcP50R^9J#&Py$jWoE}9%oo~Wp!g> zZ$WR6xhHStnrFOqtmK~)mn+nA}3La|m5d zw2LIz7KaFQiJSqN$KWW#PHi_FxCgvez$vcz3!5%VZ5DH^xn4SbCQ;r72j?|5E9Kx+ zRbe69i910_f>eS^Kb6pV5!OD#;PA8(N;*uZ^y~@fCKoKALFXmo6(BJTorzXm2bAGI zI_!(NtPXfuF}HK?L;ZVtbbq!6x@yX)M9fv{ohr+a)u`3tqaJ#ZPkC?*wmSaId*fiR zW1=VZpOUuSk-VN=)+{l$_n!W%G^zW|i&b*d)C#DX%h@K!OZ!r*b%iBe!S9eWpj4g< zWkQcpX+h8QS%;|pNIe}%h`ioPb#25Mh@Ji`(xD!65ZQUs-Ix@0aEwqG(BJCK@l904 zN-@HJvex26V06)WPtGX}l&A9^i`N8gXI#!#mH3?Q(EjK7Ae#%1$2Mz7;tr+a zt@Y0O9@EBO&43N$UK@hm3{eTPrdoKUNC_v-FUr>RK*rFE7uR?ex(xbYKBNuBtwua0 z@b3OyguYlTlvr}3p%15;G8YvP>Sy)FMFGbPt2}(T`Q_In8Z znwQpO5y?8|$}YAe4rN#SGm>53&d}&UzXk;;7#r$sB1>>nuCGBDx{thpUttU){@J=$TX?*6f39?8&KHX6 zGki)y+{6&>aDSS`P9?W0YQI1f5C_V7l($s`EQczV6bzEV`Vza?M10(GZ0BHX#Y0r@s)RY zhoquLB?@~qP|$lwZk-x`rR6y}!1%3p9?`r|u093Sr~T|pp5fxedd~tz-)uYuO7~6V z;E_A4onCF0hs>|BaJAx-1|iTrbapZQ7uaS_&PNH&|;;UIxTdigsU^Hq5st8 zOZS2eiUonJE_h44?aqoq8<7zySW$Vglc1opav34A9^2Pf0g>bYW=X zS&}nqF~KFH)~-h%Dc?l{B7ag>Z8Zkpgsqi@D44zkxZy z0tpz)9699z1=X&X*XScq&@8|znJRHSxbkek7-xZ$aF4jlilHnAr5>lpjpZb$0fozF zsbC#BWzt<7WpYC(hVItmxc|J!nBPi#%p$Z*UadHTTf}&iX?v%p`iZ5%*N;Dc^yiOE9{Ps`8u7mNHE{5j{t7251>lp}7t_An zdANFs5*bO{M7}EaDH4vR=kZ9=k}+jCitj>qei-GZo{T8-iv{!w1n|f~ZNm)ev+5Xj z0QguS*%l30>P-ZuLGmh$Pa_>3sqcVkCCY^+1Q*uID#PD-wLgtKM1sqnEK6(niQVir zq~t*AT9iXf>`7d+jm_bk+8r#I1so}5l+K<+n`l{H^yZsQ3i#GCM5PbYL56Z*f+Ola zGlYRh=s&zUPa$4VxI0g?xXC?Xp!pe=hh~@>H+!F#odUNQ>;di*L$7GN~kgShQ+#bKNaBHdRyWgXecit$?+t4%js!OUUav2H6Dv@nNqT+06=o<&tOj!?K!W?8! zSS}&T3(EZ*CoLw-PXO8}ZEYT$;ZH%X5lZkYYFNSXQyn zpAIzDjJSPwfnbeXY9I~DiclEmV?nRvqrLVEE$TDq3@F`=M1gL<+F#J3`Z#~^SZDMg z#$k9UpSTE?0dq(i-qHayVelG&L$Ee@820pY+lw8B&_PxQR!B8Ww69Wv0^-n>f)SeU zgvdeh6_(n?(-AmRfH)NY(k6Wi&|Yd6Fi#7eC80tPv63O!a)kE98{-pvXOA8u$K!(ygW>@AtlaJLP9}ukFOT|&ze1fgEzZ$-h z$eQqhfO~dtWy-<^EHyS6LGU{AQ&OvH*Gb`UkF5a~iJ{&=bGlCkv4Xo|7v|Uqb@E+F z)-@A~kyU^+CKqguCf~Sur}wZwr&aJ9Rce`Cr?K=*JNPY>3Fpa|)YdQ6_b@Phy5%`RNAL@Ji|UQ?C{#J5ynQW7^-Ij=Zj zo$8_N(hK)G&z|aqVP- zqf)Ujoj;U>5n1*vVTT+8;p!T`gP4N5+h|m-GCUTl4J!h=GXY1TB!Y;rhXQfIRtA*~ z36i1XNn})jM4HYQZ%A>hX`){uL`{}HAWp3jmW8GQ1+-sW-q=hPkZYy&rM_G1tgJ0RPVNe4 z1!k<={uhl!hR3DX>VHm0-H+q5+tY8l4iPI0M2dclrSUW_s-JbHCfZqBS>3)+JiGUp z)=DtVyoaB^|7dJ#wLJG)E%or2_`E(5CI=cZT*_|$vpjs?x9`==uK6rIm6gN}91%5Y z>m0>g1qsdTTC%P2hi;F~=8NmHKGq6i6!YdPbwQ6=?>Gn=M{K7v9&`r>G3u zY}#7zR&7#}bKOT>c!O88G0S`AJ|Vo}d*jKGL;~?O0;DGTz)C6W9pG&`3r_@s^M=-4 zC9Oo?F;*C7O;}~;gHo>LmBdmkFg6xQd^J`?F$~f~n_ReN<*HEbC_3d5qcAcR|A5-xC)9ZYA4D(w=IK(2|v(T;!va#qPR*p3UuDHfLEcleFC0LICK zym37odywaJX>DHrOB^RFs^V0*j%Ak>Bpt|Oy8Z{PH=hrfn!N@&z;#?4ganPUB-|b9=ow-IXgRSCs{qxnYq42T=MUfV>URYt;eF z*$O2VW-{>pbOUf`WX&*+iq!&WMBbw@LrUgZ;H%Wmn^AuN2W=aYbv2qk)%Eq>avmCVabKb8CRCNE&rNO#zJ02-H^Ta;}7OGWzXWUb3DB ziE13IzpKB^n)Ua*z0V$qZ129YyMS+DC^VsBSyyA4)0hU_=%Q@#tcW+^7oq-2V z4d$93xU(HDO_@AKNg2LsUYj6L5+hMvp!cZ%KuhEI(`HYBUXkVqWqSt}6qQI-2B7ma znAv&J@YYs&mEerVkw%fCve|8~*iH(SU;a zH1&b-3jhd91i{nZa2jExEU5s)iiAUMrwwfr30BwV`&&9mKDvE9bF{UMJZ~Y4yYpQL z1!Pn3PZfG70CFjXke=oh2=3>)^90D+LS3VYvcQHZt0i~Kt6^{!RHeWqX?3)kBS9W# z1gIm#3&I8DQ9=~P%)UtvvNpy7Dz*TI5EfYW2sKVekE8=)QfHygm0L65f1-G5c?d>; zH`F7o(<}D7tX1#0e}XaxeA~04W>hp(YN}~$o@Bx7>{5qxOLXE>9ty`G5E%E2m2;)C zToAe}^CQVRs1Ko+b?~14CeQd~zlo!sIbJOKzAm_WEc*r?NOyj5eZ4~i;*IVbGmB#M z)4^QZd(2j~ky0`gBhN|T+TNw!0v#e*!V~CBJ(D5 zpzk|>M96Ju1=MGlmm&$pAz-XTK8qqsr&g4;QmpCnNQj9BO~@d*kldxS>=U5Gl;*T> zmKsEHrQ3O>@UtvZjU%g>zJsl|Mva{&Q9N-dCMH@o{Xm>y*_xJZ*hBW)e0py{qFa4Eg-i` zgw?PtxivN$8TOMEj@7SQad+-0tRmpKQPxeF#sM1bf4_6 zvo5_0{>jRkWPjqFm1%4u6yZpvqmaIcsNjUScl1L&FQi-8AMaMmj4GubpOkGbFsCH$nn?wUeLz0Fq<@G-a04VC+V^Z$E zl%$`4tY;z#`1^vD9D5S?a~vZVRAZ7cyf1{e_bSV`UtWLh_G?KY1jLk%jNAkwQ|z&W z^y&U8PoBd4tY@Mdn6D`J4c&-;l%Lp%D}7lFf0n)plQ}O0NqI%#lzyH*zeLay4FJdw zK$iAlu-2@wfp|;1%8K%QR)po}AW@@b!=h1GDLr5O;>>9AY~aQ>Txfx_R<!UX6$#i2t$CL>6RIRhO|i z)PjrR&H~@Xq28u7KinL7z>vm1qii5hs;I`@8$CFpHyHQdAQPoc9C4lXS;m!jF}VO{ z#&KeMW^o-CfZQT2331XQ^a@<5=qV`~g^g=5!-RW@XZbz`%@z7Sz2^@O>m__&7Uo+G z0;E9Xl25&hvtV5CCi!{}ZIVkLJmq0`R;U0iCKj9_>t``FRlzoi)MWqq!67W(LC0V4 zEGWA+@q${^NKgkG}2;)05a>#-E7!m zNB0f!b%U(Oqs2!y-XT4LfQ37#G{!}9-uWgTbyU`Io}5xB4trO={1V^>5a;bNZ31R=pmMd; zsgPjtoFW-H)4o<{i(;C@iuQJOmLx(4L~v&7@OT_n0l%@ah~I4P?d@@G6;c4UfYG(Z zO(@V}bJ+@w>=?6od?tXyo!gF*QO3~1dyWyg0YYKT7>CQDQ0Bf}(yM1u1|0ON?w7=w;AdT(I z)3Ye8-PJ`RnA92yFhxGUBvpx-<<*CaMY`km6}u9=gvZUvO>pSk`=)=Slpql<^(#vj z#xnvI^cXo`xa*&KGb03+8*WL@$o2EHDe6u0q>D?n$gDwG~+=( zd=Pk1GVB7FkIX2=lh_HA8Y%u<(g=Vz8<86iSL7G96RA^}351#02?9_G7bH*Age;S| z+j)`H8*3;5b9CKm?-0CokY|{;J)#%VlCvzoOEmEc4}rr77$Tl$-|lyKb*C<4lH47_ zI5-+NJ1>NHCj!OHlBb~Q16#1(Wi$k>sqMNircbx_a-x)aURUr^+hj302`iXv6(nY7r_dAp6$V%+xh`G4QA}(V6N|I+E09W|d`J-7 z*_K)nF;ubr^6K{E&Q%rzN2PsrkFe}ocXLLeJ^zhW;VpMc6|u-~qO*27d%S=9l90<6 zIwN&gw=zSV^c(_gcg=PMYMUyrbtz~sOm`P|cbAJVCo;2ErMO^PlMD>$w(cXO2tEcG zvR8|#GV%0-&eYu0rN!0TiS?!zt@^COkB~M)&!hjY{ufy9RBkmJwRMzanEVS8C|l}j zCZA^=W{=2ba12H#Tf`m>wm`EQ`uPvXTwQ#}!4WaX^Da z`l~Be&}B*sL63*26-(sdnUQ2wc7(&ohRKizYoF8!)OW;w_ze6J(DLG1ZU0XH^DNDQ zpYmvALe;h>M{nP{Gxpl-Y^#Es!Lb@054JmOoeRe@UcNmxR-C$VWn%nxp#G$~WtW~* z-+^ASR0cnd>SD?2nQSb>Ei4#kLFfK_u=t1>+*h4V9r+Pd1m{9HC;?rI6axDhRmUzU z*QuV6Ah#LRP-O+yVdh|GT&Xrm-`0FGjbNOHir7Zkf3TeqQ?JC~qu;ytZE3yzYx9v+B z{Ni^b6KpuCcG9-m{26y{GW3FF54B;PtvM3MSVz+afz`8m9jA}?W~x!@Tzenv=trT< zxCmp$^o?Rd#6D`!M&|Xz`)BvWeUhQ&4uRZh>AZD&t@YrQ{rTzDM|Yiq3D*tMjB;M^ zEXdQy$LU?@zoE?@^EW#aez`qEvht3cwI_X?P!ODkh=Lo}-ssjT3LT#A8QUvRX@I{t z-$Q3^Fv^}Q{y#NvZQ%6Asc#*gJ^AB9%@eDG#pCZk`a?%rhyHHi0Y7?^U;VQOZ<7$6 zN-hS4_*nop@kTt<_jYG*0WYDmv{D?CudsHWe~UwllNjt6dnestE-c314FvMdpm?L7 z$@|-0g~h;r9xkFI>oQVMOKi(?u$83Mf?vAUalF1Od^tbFoRNe_61a`ZW9-l}Et8sdOz&Vs#f-(RI`_0bCTP5c3t zqO&5aaZjP6b60q}h?4nfH5kdhULib8^8TrDtrx2`bmtt637bV@M73&m(D;RA+bdSy zNHncjBH!0aW~t`8bNEu4?X*%PcVF&ym)BKLgB6K<*{&YIqFD=G+<6Pv4o;&|o}Oan zryON;4+an3!rG;PL$b*LCJ2O>8`bb7F+z5A>bW=L#=n`NiZ10fK zxxN5zh3UjG$AJfNHBG3~xh_^N!N8ar$i*}tu*rwcPB4XGSrmyj5fOfHG*5>5Kt~%Z zC9Sp=xM*a6;}hP=Qy*^G^ZL{P2#ABZ{_4SLExSL+46|(x! z{h4bw>1M)~iGK{o6c3ihD58no^(FbT{3jYC){A>$KZKeLV?@6Wf@Iz8R0U8OD0gl` zyKbB~%(DK;gXQAm5NAU>PD4OMsEoNHbB4;T=G6cp4Tvk({jq~#mf*lo9<(w!Q*ICs zs@r(J%YpJlO)my1w`;P&Snz&A&s@P8qyExIpD;bhkXTAxV(Fh|CxdQ_jZoQOuB@$n zk8XZ!7YmFav!+-#;TqaBOVIj5;2-onUFObOBQt!~#^u^_!;Gzf6|YQPygoG%Jac5b zh`1O5AO)G53f0hX7>xEN4o;(dbz?)n`^ zFb7{;QO`x3H9B$UM)At{4ROQ;9Cf2tN2gN^cGIJiql_X?-<-NVQ=GUlGjpA<x%@Cvn$G5d1v}=r@qzPr4z4RWPeq&|3XLLzn4 z7pY8kp=4zxG_mHdrNYi&54=)F^oV4EDc#bXlPe|~)()k<|MdrhJmSF_{}D5(jV|G_ z(NY&aolL87$|p{zGc{3w6g=#|{?YF!Mx#Fj1yZ+!qQOLk*^flGKCoBnCfDtwU$W04 z+iFSWFm24fV*e}!(P64V;f6|m^C$%p>G&2hk7W31zj@Jsx)ST894(VlxoQ>?vN)2Q zJ2=id^?iJzRwlt%)>cMIO{c}86_R%2Mco)zkl2iV=qc6nMT!Gt)YZ70`>*6%(gQ{8 zlUK_p%Md3=4$d4LlhRo9q#>Kasy!n|Zv9mtGn?cmXJ1(*S9sMDbMbj)TTu8q$D4bG zN+6eR1;56d$somRk&5KPyhKF8%I~NHy$xc9w_-9AIHNf7Y9gfub3U9%2xJXIVh7po z!;}maHpLg1dyAuzCmU%ouy8)bVR^Ah3R%Hx!>wA}Q7jRBULfCc_|Tf<*E*%`Vr_>YeN)#D#M z_I<~OkNoE0-#PTFg&*O^f6Bgk-*E8U;6SA}H9q=&@FkdGCO=UZ6ChnPt!DCs{eRMA zJ+rb`9Nk+kmKsHZE^D$bqV-E9lSj6bW=II?TcT;M0p!{^58)2j>x6^rWce5S&mRg8}rdfkH$Xh~{rNg#Cukx3YC9EFia z`Hy2W^(3qdmlx+H1z|Jwn|S4Fd}4|R@7IZW7q1McjCi0#X$M4nnWNuo|7mEVmR7lj zRVcTLVH0({Mlh6QYn5_zQhHx^@IKb4cih$p&K-yzpw0sCjX#>rf2ZbY?cXHry`?f+ z=$fbB#Iu#6XZymzdyfxPZrPIo+)QtW0D>VQgO;zg7L*BmlexU+y7Q?OZ)<5L=@~%b zjx>NOhGgK`9}gK?cw)K-@8RPc@yClZ6<8t>6xB07-v2~mt8(*Ad}1|v;r>St-mO+7 zuWc%uze_62h6|NTvHsdHtA-ol@?-Kdl(+=OG*QbAr+np*Y*Ks&OwpBbqlB2HfdfCr z62S6+1x=(3GER?x*`ExOE;6#`5R|Ob#gL_uOP3?_Qjx2dZa#Xje1GM3jPc#HE+dRy zUKs`yt6H3j{L;bKu~dF63R7YRi&>kB>c-A0qY@W|E;_os%0HF^kaNg)Sh?aWO!A$> zzgh!I{tHYT{yi+UVjeb;1nY?(TWHMY!%Gw_7Hb{}e`31qu4ok1L1JZMRDQ`=`i@Y$gkj0Um z4vjXfH$oWws;^^h+ANfta@o?0KtSip4TOzR+}kf6yhB>Z199(#@{_B;Ty+z$C@Td46eb(!9o$Tx#SC3Bx-8>^4214cCIGf4rh7K zpI;;e&;=XT5+&r2vSw zyr>y_o4_t^9K0O~@?n>8U$U6F!;Qa);wE!{*+rmJVG-m~S1EIb-3X+y4S7(6Yg6SL z%iWEauYkelfErZnq|F5R&rAOp4QH7CIrzZApJ98y;$^6+r7XJewpyE@iYC^dtZpvf zsexDBP82=ca4W_2udnXvAgT@#Tf;?4WD|q(jS50hi8+w;FiR)`D5}%+qL2gNDmVN@ z=D~oq_{J_rc0K573o?E|V&P$zG==aIY4&8Qi)%yVt{rls)Jn)lTM{90MF5pHVRa=3 z(i5E8)N|dOnojPJb?D~yq>0WWvYkUj_VvM?fiu5+=10%`l`~ssuAh1S%#qXo{Pd5X z{+`nhPv1U${`AnPUqAIzr~ac;k5An_RXO#R;eR#!-wyxn;cp)P>~L%N9VdV5deQ0%ImU!*hCqHy}ybwBw zvn{CCVP`CZ!A(jQVv9<-+#K>qs_E~7=duQLd=2Ofvf%I=*QyKtH3F9(5vU3{(Ibw3 zy-}X`uOT4-cWH@!#8#~-Jh5h@+??~T(O8@9zefF?Er>R&JO_W2 zDs`Y6UiYtoSp<^rGB;?!exQUS*ZSn<=HCVq%X$MwQiyxtM}`mvxaoWTZD@yv zI9@2~`~mU>+uJI?>fZ*MK^WRwRc-(gn26OIjaU5Jh#zov?S zRiP6m@n5A-0yi9ll6U-TDq=Jg%}|rlHMJl}AVwN9{xyt^L$y%@te+RBD9NoF)oK44 z#;GBZsZdX1rG;Bg9-w^NzXmiM2;~tAr4Lyk=rl%Zx4c)UnTtGpz0wlzQK?>q3$5H5 z^lrltLt}({a2N8|&|*TCRI1$cuhE9WuMUqaqn2fe;9CgUl>bp+^YV;jY9Mbxs3{FL zTQ~gM&<5Uwjg*9%HXZCRl7^RE#G6_h8?ay7|Nv&=%{ANQ|;9fgCy+H!CpL$5YD z_N}Y_HKo})t)@d_emhu7!8W}AE6tB!>3fPjEwo$pqpSzY1ZL{NB_WAPw!CmvVRS_sV)IyN2#hK zjurlW$-joY5--G_(s9kFz=X)%F8bFnf5|3Ezv<`5bQu8~^{;^-kvB)@2&@RYdm?k{ zqW(2y#2uYoF9RCEp|Wgz>4JX^x{3E~!ZyHDYlSMcX6qaMYs41QLYJZ{*#h(!cP)SF zxx-gtGr>v60I;|m7{T_zQ(8Ob-9>2ZR4c$i7H1J}#!;^|zrnwz${x7@>i@%q-x)alJ*PH@|MBqf&@T_YIQT2a|H-lWBfon1^}_EI_69x? z8ub3ySGua9btr>iYdL|OAT*5B12wh(r@eFAZKI09Fz2>FQn&yKA%r0m8fe`akH?1s zYDfj8jani~D^&Th)UzQ1d|ModMTsEt}JUGro-acY` zz*)&rtX0pZ;8ip?W{ zD%g)rHD*4Wz75@O#Uz6pX6dov zYgEdp$fpGJ)2z_Kyu-*X*BgoyCcvyEST25}Ii2clSP!Oy;b; z43oKlz3Rz9-K!6dXma0=?S%}RM?`=)hpN=pX8uM7bE@l(!Cav0VS<+GO$}aC*I#m7 zmzt_%7jsOw9OoemY1l`E9Jq)c-mFNlsIdxpfo@h47#XO?|802uf(CC)dd5nNu`S`(8A9qvA+3nQF3)rfX95wy+;8kpO`$aRQL@bDt5yv5{ z!xOGr8Ox3ftWy@Q(jdvPax6Puz{-B&ipu|=d~D;P$A5Wj`rCU^JQXrjZ zszUS$DkF@Z^Z&!<@EE8>Xk!I~x0Bv0?&lLUrT5huq-eBzq%{xwh!bZM$@^^V>V?>^bg#1PUHIMqD1G+-(tMd(-sx1UW}jfkP|qh->phVvOK z83{d1WoV!pWf5Chq^7ZwBNpz%+()xQ0tb}XaqG)8x(;dzqz-0HL8oDFBS0XAS1-%z+r<5BfrA#uNTpZcInR%)S*e!Myof+3h$uUa%R^n;=StxYD ztabUbtXKK&^>p~q#cyG55x+&j-A@r=?m6R?VOZ;>h*Ctsz+U?#os_yHk4W+sIhw}I z1tj@-1$P!&r9>zrnM!U{u(oY_8G~n8wbIC7vsj%G4jZ%6l3UcMOLDETh=A$p5vF3A zlA+YNe9v2JZ&ImynWG!J=2n~Hk((Vu>@ey^GK&T8>rY5t7-qI^dkIPEBaME_0xwLY ziWk+IjmfN&N)m00&|gqwWeuP3{mqE-LBVZ%ZOcGpNR`kheH;2-+Ca0Cq0xIbegs0| z5&2pARvQ{9awA~e&k7uGRGUnd2aYPPbxbHKymq}VdMxbZ)pN#LK#gd#gm{eoTz z3#D?T%Z^-?Xd8OLAesdKh2kRm;C8pg7cyUB$#Cp(RPv%=A0u65^kH1HMh@CoQ5Lql z#0q>dPewt7Z>Dz-&m=}j6KcjT1M^f;TF2U)vNUOS*1Z-bfQ)1}zYtOCDCiwBJ4dxX z1)?CGPzeQCnYqOJ`H(TPw$`s+Wl(5gwT&!HS`!n?F0}L_jJFoYjD96Mrom;vb}4jS zQ938-TqB~UP|!PLJ4eR132q8tM?mz;uikyRmOf!tHUGpus0M`Si4{+Z?^;I|nA1xS zEWKg6XJ}lrjgPi3+*}QJPP>S$Vs9X1!2-ES=<7yN>Ck+;`L}#qnL_E0h#~+0Ffy2?|`SSz*}J_B1~hHXzUrG zuE(-B?)b821;JFauAOv2W_;;)B;*VZSg(M+D@n{x{{O@icOH7W{?wy~zCYMK@Z;et zlfO@XIr;p=4-+R29OGN#Ut~7bHEYn5;KrPdtgQHXdS!|(; zfsa-i;T-jiI@e4r$oaPTm^NQQIM{hISvT|j<4K)4dFqz=e0rBWTrNVKj|9gN$V^B$ znG2_6E9w)akQ1*jgE?jX<+Xl@ZTLzNK^+g$oh>Dg0|8xI1Yp?G$X;ozy<5saYlIoytxB^GFy3W5H~yV3K#YoG^iQ|GzenKC#<0+fwFsb*I@Qm)v)lBsdAgsuS^fiYt{kF zyJ4;D)oDdS=&luz?TsdmK6EJ8O2(M!A(_VfoN#isF=DJq&UJ0CMq}?f8`eMqk=_Cb zN=(EX*%f47E0wv*Z^mt=(>S<9d2QhbKeunFS&hH9v_J{RyW3Rc8PI literal 0 HcmV?d00001 diff --git a/database.py b/database.py new file mode 100644 index 0000000..eaf6b00 --- /dev/null +++ b/database.py @@ -0,0 +1,157 @@ +import os +import sqlite3 +import pandas as pd +from dotenv import load_dotenv +from sqlalchemy import create_engine + +load_dotenv() + +DB_PATH = os.getenv("DB_PATH", "data_sentimen_komdigi.db") + +engine = create_engine(f"sqlite:///{DB_PATH}", echo=False) + + +def init_db(): + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + + cursor.execute(""" + CREATE TABLE IF NOT EXISTS tweets ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + tweet_id TEXT UNIQUE, + text TEXT, + created_at TEXT, + crawled_at TEXT, + crawl_type TEXT DEFAULT 'realtime' + ) + """) + + conn.commit() + conn.close() + + +def save_tweets(data, crawl_type="realtime"): + init_db() + + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + + saved = 0 + + for tweet in data: + try: + cursor.execute(""" + INSERT OR IGNORE INTO tweets + (tweet_id, text, created_at, crawled_at, crawl_type) + VALUES (?, ?, ?, ?, ?) + """, ( + str(tweet.get("tweet_id", "")), + str(tweet.get("text", "")), + str(tweet.get("created_at", "")), + str(tweet.get("crawled_at", "")), + crawl_type, + )) + + if cursor.rowcount > 0: + saved += 1 + + except Exception as e: + print("Gagal simpan tweet:", e) + + conn.commit() + conn.close() + + return saved + + +def load_tweets(): + init_db() + + conn = sqlite3.connect(DB_PATH) + + df = pd.read_sql_query(""" + SELECT * + FROM tweets + ORDER BY created_at DESC + """, conn) + + conn.close() + + return df + + +def insert_tweets(df): + data = [] + + for _, row in df.iterrows(): + full_text = row.get("full_text", row.get("text", "")) + + data.append({ + "tweet_id": row.get( + "tweet_id", + f"{row.get('username', '')}_{hash(full_text)}" + ), + "text": full_text, + "created_at": row.get("created_at", ""), + "crawled_at": row.get("crawled_at", ""), + }) + + return save_tweets(data, "realtime") + + +def get_tweet_count(): + """Dapatkan jumlah tweet terbaru dari database untuk cache invalidation""" + init_db() + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + cursor.execute("SELECT COUNT(*) FROM tweets") + count = cursor.fetchone()[0] + conn.close() + return count + + +def get_existing_tweet_ids(tweet_ids): + """Ambil tweet_id yang sudah ada agar crawler bisa melewati duplikat.""" + ids = [str(tweet_id) for tweet_id in tweet_ids if str(tweet_id).strip()] + + if not ids: + return set() + + init_db() + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + existing = set() + + for start in range(0, len(ids), 500): + chunk = ids[start:start + 500] + placeholders = ",".join(["?"] * len(chunk)) + + cursor.execute( + f"SELECT tweet_id FROM tweets WHERE tweet_id IN ({placeholders})", + chunk + ) + + existing.update(str(row[0]) for row in cursor.fetchall()) + + conn.close() + return existing + + +def get_latest_crawl_time(): + """Dapatkan waktu crawl terakhir untuk mendeteksi data baru""" + init_db() + conn = sqlite3.connect(DB_PATH) + df = pd.read_sql_query(""" + SELECT MAX(crawled_at) as latest_crawl + FROM tweets + """, conn) + conn.close() + + if df.empty or df['latest_crawl'].isna().all(): + return None + return df['latest_crawl'].iloc[0] + + +if __name__ == "__main__": + init_db() + print("Database berhasil dibuat.") diff --git a/hasil_analisis_sentimen.xlsx b/hasil_analisis_sentimen.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..fc689407b7a000edd15c85ed4f43106ceaf76560 GIT binary patch literal 62807 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%0AUv8)Ou*wQzPl;07|6M2|N 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 NxoTLmCgSbP#O8XH{ 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 AK>z>% literal 0 HcmV?d00001 diff --git a/import_data_awal.py b/import_data_awal.py new file mode 100644 index 0000000..689e66a --- /dev/null +++ b/import_data_awal.py @@ -0,0 +1,113 @@ +import sqlite3 +from datetime import datetime + +import pandas as pd + + +DB_FILE = "data_sentimen_komdigi.db" +EXCEL_FILE = "hasil_prediksi_sentimen.xlsx" +TABLE_NAME = "tweets" + + +def get_value(row, possible_names, default=""): + for name in possible_names: + if name in row and pd.notna(row[name]): + return row[name] + return default + + +def parse_date(value): + if pd.isna(value) or value == "": + return datetime.now().isoformat() + + try: + return pd.to_datetime(value).isoformat() + except Exception: + return str(value) + + +df = pd.read_excel(EXCEL_FILE) + +conn = sqlite3.connect(DB_FILE) +cur = conn.cursor() + +cur.execute(""" +CREATE TABLE IF NOT EXISTS tweets ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + tweet_id TEXT UNIQUE, + text TEXT, + created_at TEXT, + crawled_at TEXT, + crawl_type TEXT +) +""") + +inserted = 0 +skipped = 0 + +for index, row in df.iterrows(): + text = str(get_value(row, [ + "text", + "full_text", + "Isi Tweet", + "tweet", + "Tweet", + "clean_text", + "teks", + "komentar" + ])).strip() + + if not text or text.lower() == "nan": + skipped += 1 + continue + + tweet_id = str(get_value(row, [ + "tweet_id", + "id_str", + "ID Tweet", + "id", + "Id" + ], f"historis_{index}_{abs(hash(text))}")) + + created_at = parse_date(get_value(row, [ + "created_at", + "Tanggal Tweet", + "tanggal", + "date", + "Date", + "waktu" + ], datetime.now().isoformat())) + + crawled_at = parse_date(get_value(row, [ + "crawled_at", + "Masuk Database" + ], datetime.now().isoformat())) + + try: + cur.execute(""" + INSERT OR IGNORE INTO tweets + (tweet_id, text, created_at, crawled_at, crawl_type) + VALUES (?, ?, ?, ?, ?) + """, ( + tweet_id, + text, + created_at, + crawled_at, + "historis" + )) + + if cur.rowcount > 0: + inserted += 1 + else: + skipped += 1 + + except Exception as e: + print("Gagal insert:", e) + skipped += 1 + +conn.commit() +conn.close() + +print("Import selesai.") +print("Berhasil masuk:", inserted) +print("Dilewati:", skipped) diff --git a/indonesian-stopwords-complete.txt b/indonesian-stopwords-complete.txt new file mode 100644 index 0000000..bf88a45 --- /dev/null +++ b/indonesian-stopwords-complete.txt @@ -0,0 +1,758 @@ +ada +adalah +adanya +adapun +agak +agaknya +agar +akan +akankah +akhir +akhiri +akhirnya +aku +akulah +amat +amatlah +anda +andalah +antar +antara +antaranya +apa +apaan +apabila +apakah +apalagi +apatah +artinya +asal +asalkan +atas +atau +ataukah +ataupun +awal +awalnya +bagai +bagaikan +bagaimana +bagaimanakah +bagaimanapun +bagi +bagian +bahkan +bahwa +bahwasanya +baik +bakal +bakalan +balik +banyak +bapak +baru +bawah +beberapa +begini +beginian +beginikah +beginilah +begitu +begitukah +begitulah +begitupun +bekerja +belakang +belakangan +belum +belumlah +benar +benarkah +benarlah +berada +berakhir +berakhirlah +berakhirnya +berapa +berapakah +berapalah +berapapun +berarti +berawal +berbagai +berdatangan +beri +berikan +berikut +berikutnya +berjumlah +berkali-kali +berkata +berkehendak +berkeinginan +berkenaan +berlainan +berlalu +berlangsung +berlebihan +bermacam +bermacam-macam +bermaksud +bermula +bersama +bersama-sama +bersiap +bersiap-siap +bertanya +bertanya-tanya +berturut +berturut-turut +bertutur +berujar +berupa +besar +betul +betulkah +biasa +biasanya +bila +bilakah +bisa +bisakah +boleh +bolehkah +bolehlah +buat +bukan +bukankah +bukanlah +bukannya +bulan +bung +cara +caranya +cukup +cukupkah +cukuplah +cuma +dahulu +dalam +dan +dapat +dari +daripada +datang +dekat +demi +demikian +demikianlah +dengan +depan +di +dia +diakhiri +diakhirinya +dialah +diantara +diantaranya +diberi +diberikan +diberikannya +dibuat +dibuatnya +didapat +didatangkan +digunakan +diibaratkan +diibaratkannya +diingat +diingatkan +diinginkan +dijawab +dijelaskan +dijelaskannya +dikarenakan +dikatakan +dikatakannya +dikerjakan +diketahui +diketahuinya +dikira +dilakukan +dilalui +dilihat +dimaksud +dimaksudkan +dimaksudkannya +dimaksudnya +diminta +dimintai +dimisalkan +dimulai +dimulailah +dimulainya +dimungkinkan +dini +dipastikan +diperbuat +diperbuatnya +dipergunakan +diperkirakan +diperlihatkan +diperlukan +diperlukannya +dipersoalkan +dipertanyakan +dipunyai +diri +dirinya +disampaikan +disebut +disebutkan +disebutkannya +disini +disinilah +ditambahkan +ditandaskan +ditanya +ditanyai +ditanyakan +ditegaskan +ditujukan +ditunjuk +ditunjuki +ditunjukkan +ditunjukkannya +ditunjuknya +dituturkan +dituturkannya +diucapkan +diucapkannya +diungkapkan +dong +dua +dulu +empat +enggak +enggaknya +entah +entahlah +guna +gunakan +hal +hampir +hanya +hanyalah +hari +harus +haruslah +harusnya +hendak +hendaklah +hendaknya +hingga +ia +ialah +ibarat +ibaratkan +ibaratnya +ibu +ikut +ingat +ingat-ingat +ingin +inginkah +inginkan +ini +inikah +inilah +itu +itukah +itulah +jadi +jadilah +jadinya +jangan +jangankan +janganlah +jauh +jawab +jawaban +jawabnya +jelas +jelaskan +jelaslah +jelasnya +jika +jikalau +juga +jumlah +jumlahnya +justru +kala +kalau +kalaulah +kalaupun +kalian +kami +kamilah +kamu +kamulah +kan +kapan +kapankah +kapanpun +karena +karenanya +kasus +kata +katakan +katakanlah +katanya +ke +keadaan +kebetulan +kecil +kedua +keduanya +keinginan +kelamaan +kelihatan +kelihatannya +kelima +keluar +kembali +kemudian +kemungkinan +kemungkinannya +kenapa +kepada +kepadanya +kesampaian +keseluruhan +keseluruhannya +keterlaluan +ketika +khususnya +kini +kinilah +kira +kira-kira +kiranya +kita +kitalah +kok +kurang +lagi +lagian +lah +lain +lainnya +lalu +lama +lamanya +lanjut +lanjutnya +lebih +lewat +lima +luar +macam +maka +makanya +makin +malah +malahan +mampu +mampukah +mana +manakala +manalagi +masa +masalah +masalahnya +masih +masihkah +masing +masing-masing +mau +maupun +melainkan +melakukan +melalui +melihat +melihatnya +memang +memastikan +memberi +memberikan +membuat +memerlukan +memihak +meminta +memintakan +memisalkan +memperbuat +mempergunakan +memperkirakan +memperlihatkan +mempersiapkan +mempersoalkan +mempertanyakan +mempunyai +memulai +memungkinkan +menaiki +menambahkan +menandaskan +menanti +menanti-nanti +menantikan +menanya +menanyai +menanyakan +mendapat +mendapatkan +mendatang +mendatangi +mendatangkan +menegaskan +mengakhiri +mengapa +mengatakan +mengatakannya +mengenai +mengerjakan +mengetahui +menggunakan +menghendaki +mengibaratkan +mengibaratkannya +mengingat +mengingatkan +menginginkan +mengira +mengucapkan +mengucapkannya +mengungkapkan +menjadi +menjawab +menjelaskan +menuju +menunjuk +menunjuki +menunjukkan +menunjuknya +menurut +menuturkan +menyampaikan +menyangkut +menyatakan +menyebutkan +menyeluruh +menyiapkan +merasa +mereka +merekalah +merupakan +meski +meskipun +meyakini +meyakinkan +minta +mirip +misal +misalkan +misalnya +mula +mulai +mulailah +mulanya +mungkin +mungkinkah +nah +naik +namun +nanti +nantinya +nyaris +nyatanya +oleh +olehnya +pada +padahal +padanya +pak +paling +panjang +pantas +para +pasti +pastilah +penting +pentingnya +per +percuma +perlu +perlukah +perlunya +pernah +persoalan +pertama +pertama-tama +pertanyaan +pertanyakan +pihak +pihaknya +pukul +pula +pun +punya +rasa +rasanya +rata +rupanya +saat +saatnya +saja +sajalah +saling +sama +sama-sama +sambil +sampai +sampai-sampai +sampaikan +sana +sangat +sangatlah +satu +saya +sayalah +se +sebab +sebabnya +sebagai +sebagaimana +sebagainya +sebagian +sebaik +sebaik-baiknya +sebaiknya +sebaliknya +sebanyak +sebegini +sebegitu +sebelum +sebelumnya +sebenarnya +seberapa +sebesar +sebetulnya +sebisanya +sebuah +sebut +sebutlah +sebutnya +secara +secukupnya +sedang +sedangkan +sedemikian +sedikit +sedikitnya +seenaknya +segala +segalanya +segera +seharusnya +sehingga +seingat +sejak +sejauh +sejenak +sejumlah +sekadar +sekadarnya +sekali +sekali-kali +sekalian +sekaligus +sekalipun +sekarang +sekarang +sekecil +seketika +sekiranya +sekitar +sekitarnya +sekurang-kurangnya +sekurangnya +sela +selain +selaku +selalu +selama +selama-lamanya +selamanya +selanjutnya +seluruh +seluruhnya +semacam +semakin +semampu +semampunya +semasa +semasih +semata +semata-mata +semaunya +sementara +semisal +semisalnya +sempat +semua +semuanya +semula +sendiri +sendirian +sendirinya +seolah +seolah-olah +seorang +sepanjang +sepantasnya +sepantasnyalah +seperlunya +seperti +sepertinya +sepihak +sering +seringnya +serta +serupa +sesaat +sesama +sesampai +sesegera +sesekali +seseorang +sesuatu +sesuatunya +sesudah +sesudahnya +setelah +setempat +setengah +seterusnya +setiap +setiba +setibanya +setidak-tidaknya +setidaknya +setinggi +seusai +sewaktu +siap +siapa +siapakah +siapapun +sini +sinilah +soal +soalnya +suatu +sudah +sudahkah +sudahlah +supaya +tadi +tadinya +tahu +tahun +tak +tambah +tambahnya +tampak +tampaknya +tandas +tandasnya +tanpa +tanya +tanyakan +tanyanya +tapi +tegas +tegasnya +telah +tempat +tengah +tentang +tentu +tentulah +tentunya +tepat +terakhir +terasa +terbanyak +terdahulu +terdapat +terdiri +terhadap +terhadapnya +teringat +teringat-ingat +terjadi +terjadilah +terjadinya +terkira +terlalu +terlebih +terlihat +termasuk +ternyata +tersampaikan +tersebut +tersebutlah +tertentu +tertuju +terus +terutama +tetap +tetapi +tiap +tiba +tiba-tiba +tidak +tidakkah +tidaklah +tiga +tinggi +toh +tunjuk +turut +tutur +tuturnya +ucap +ucapnya +ujar +ujarnya +umum +umumnya +ungkap +ungkapnya +untuk +usah +usai +waduh +wah +wahai +waktu +waktunya +walau +walaupun +wong +yaitu +yakin +yakni +yang \ No newline at end of file diff --git a/labeled_tweet.xlsx b/labeled_tweet.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..8356d075d2cdb8389d20bef0f058ba741fb49337 GIT binary patch literal 45279 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 M_t|*B>VMq*A8DN;g8%>k literal 0 HcmV?d00001 diff --git a/model_naive_bayes.pkl b/model_naive_bayes.pkl new file mode 100644 index 0000000000000000000000000000000000000000..a05c6b221589e9de340bb2243adbe5b4f7189be1 GIT binary patch literal 31559 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 b>#^g@Qj3Z+^Yeh$M;q#y=^0KbP0|AZKv^O+ literal 0 HcmV?d00001 diff --git a/page_modules/__init__.py b/page_modules/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/page_modules/crawling_page.py b/page_modules/crawling_page.py new file mode 100644 index 0000000..e780241 --- /dev/null +++ b/page_modules/crawling_page.py @@ -0,0 +1,1350 @@ +import os +import time +from datetime import datetime, timedelta, timezone + +import pandas as pd +import plotly.graph_objects as go +import streamlit as st +from streamlit_autorefresh import st_autorefresh + +from page_modules.table_utils import render_standard_table +from database import engine +from timezone_utils import ( + parse_dt_with_tz, + parse_dt_with_source_tz, + get_timezone_label, + get_timezone_name, +) +from crawler import ( + get_auto_crawl_logs, + get_crawler_state, + AUTO_CRAWL_INTERVAL_HOURS, + NRT_INTERVAL_MINUTES, + auto_crawl_job, +) + + +def parse_dt(series): + return parse_dt_with_tz( + series, + st.session_state.get("user_timezone", "WIB (UTC+7)") + ) + + +def parse_crawled_dt(series): + return parse_dt_with_source_tz( + series, + st.session_state.get("user_timezone", "WIB (UTC+7)"), + os.getenv("APP_TIMEZONE", "Asia/Makassar") + ) + + +def format_dt(value): + if value is None or pd.isna(value): + return "Belum ada" + + try: + if not hasattr(value, "strftime"): + value = parse_dt(pd.Series([value])).iloc[0] + + if value is None or pd.isna(value): + return "Belum ada" + + tz_label = get_timezone_label( + st.session_state.get("user_timezone", "WIB (UTC+7)") + ) + return f"{value.strftime('%d/%m/%Y %H:%M')} {tz_label}" + except Exception: + return "Belum ada" + + +def user_now(): + timezone_choice = st.session_state.get("user_timezone", "WIB (UTC+7)") + return pd.Timestamp.now(tz=get_timezone_name(timezone_choice)).tz_localize(None) + + +def user_today(): + return user_now().date() + + +def format_countdown(delta): + total_seconds = max(0, int(delta.total_seconds())) + minutes, seconds = divmod(total_seconds, 60) + return f"{minutes:02d}:{seconds:02d}" + + +def parse_log_time(value): + try: + parsed = pd.to_datetime(value, errors="coerce") + except Exception: + return None + + if pd.isna(parsed): + return None + + if parsed.tzinfo is None: + parsed = parsed.tz_localize(os.getenv("APP_TIMEZONE", "Asia/Makassar")) + + return parsed.tz_convert("UTC").to_pydatetime() + + +def to_user_local_datetime(value): + if value is None or pd.isna(value): + return None + + try: + parsed = pd.to_datetime(value, errors="coerce", format="mixed") + except Exception: + return None + + if pd.isna(parsed): + return None + + if parsed.tzinfo is None: + return parsed.to_pydatetime() if hasattr(parsed, "to_pydatetime") else parsed + + converted = parse_dt(pd.Series([parsed])).iloc[0] + + if converted is None or pd.isna(converted): + return None + + return converted.to_pydatetime() if hasattr(converted, "to_pydatetime") else converted + + +def get_visible_crawl_logs(logs): + started_at = st.session_state.get("crawl_history_started_at") + + if started_at is None: + return logs + + started_at = parse_log_time(started_at.isoformat()) + + visible = [] + + for log in logs: + log_dt = parse_log_time(log.get("timestamp")) + + if log_dt is not None and log_dt >= started_at: + visible.append(log) + + return visible + + +def is_crawler_running(): + state = get_crawler_state() + updated_at = _parse_state_datetime(state.get("updated_at")) + + if updated_at is None: + return False + + return ( + state.get ("is_running", False) + and datetime.now(timezone.utc) - updated_at <= _crawler_state_threshold() + ) + + +def _parse_state_datetime(value): + if not value: + return None + + try: + parsed = datetime.fromisoformat(value) + except Exception: + return None + + if parsed.tzinfo is None: + parsed = pd.Timestamp(parsed).tz_localize( + os.getenv("APP_TIMEZONE", "Asia/Makassar") + ).to_pydatetime() + + return parsed.astimezone(timezone.utc) + + +def _crawler_state_threshold(): + return timedelta(minutes=max((NRT_INTERVAL_MINUTES * 2) + 1, 3)) + + +def is_crawler_service_active(logs=None): + state = get_crawler_state() + threshold = _crawler_state_threshold() + now = datetime.now(timezone.utc) + + if not state: + return False + + if is_crawler_running(): + return True + + heartbeat_at = _parse_state_datetime(state.get("heartbeat_at")) + + if ( + state.get("service_active", False) + and heartbeat_at is not None + and now - heartbeat_at <= threshold + ): + return True + + return False + + +def _section_header(title, subtitle=""): + """Render consistent section header card""" + sub_html = ( + f'

{subtitle}
' + if subtitle else "" + ) + + st.markdown(f""" +
+
{title}
+ {sub_html} +
+""", unsafe_allow_html=True) + + +def _captured_section_gap(size="md"): + heights = { + "sm": 12, + "md": 20, + "lg": 28, + } + height = heights.get(size, heights["md"]) + + st.markdown( + f'
', + unsafe_allow_html=True + ) + + +def _render_crawling_styles(): + st.markdown(""" + +""", unsafe_allow_html=True) + + +def _render_metrics(total, days, earliest, latest, filter_label, basis_label): + st.markdown(f""" +
+ πŸ”„ Data aktif: {filter_label} Β· {basis_label} +
+""", unsafe_allow_html=True) + + c1, c2, c3, c4 = st.columns(4) + + pills = [ + ( + c1, + "πŸ“Š", + "#eef2ff", + "#3b6cf7", + "#1e3a8a", + "Total Tweet", + f"{total:,}", + "Terkumpul dalam periode ini" + ), + ( + c2, + "πŸ“…", + "#f0fdf4", + "#16a34a", + "#14532d", + "Rentang Waktu Data", + f"{days} Hari", + basis_label + ), + ( + c3, + "πŸ—“οΈ", + "#fff7ed", + "#ea580c", + "#7c2d12", + "Mulai Dari", + earliest, + "Tanggal awal periode" + ), + ( + c4, + "πŸ“†", + "#fefce8", + "#ca8a04", + "#713f12", + "Sampai Dengan", + latest, + "Tanggal akhir periode" + ), + ] + + for col, icon, bg, color, dark, label, val, sub in pills: + with col: + fs = "1.05rem" if len(str(val)) > 10 else "1.55rem" + + st.markdown(f""" +
+
{icon}
+
{label}
+
{val}
+
{sub}
+
+""", unsafe_allow_html=True) + + +def _render_charts(df, filter_label, date_col, chart_label, dt_start, dt_end): + df_tl = df.copy() + df_tl = df_tl.dropna(subset=[date_col]) + + if df_tl.empty: + st.info("Belum ada data valid untuk grafik.") + return + + is_realtime_basis = date_col == "crawled_at" + chart_title = ( + "πŸ“… Jumlah Data Masuk Per Hari" + if is_realtime_basis else + "πŸ“… Jumlah Tweet Per Hari" + ) + count_label = "data masuk" if is_realtime_basis else "tweet" + avg_label = ( + "Rata-rata Data Masuk per Hari" + if is_realtime_basis else + "Rata-rata Tweet per Hari" + ) + + df_tl["date_key"] = df_tl[date_col].dt.strftime("%Y-%m-%d") + + actual_daily = ( + df_tl + .groupby("date_key") + .size() + .reset_index(name="count") + .sort_values("date_key") + ) + + date_range = pd.date_range( + pd.Timestamp(dt_start).date(), + pd.Timestamp(dt_end).date(), + freq="D" + ) + + daily = pd.DataFrame({ + "date_key": date_range.strftime("%Y-%m-%d"), + "date_str": date_range.strftime("%d %b"), + }).merge( + actual_daily, + on="date_key", + how="left" + ) + + daily["count"] = daily["count"].fillna(0).astype(int) + + ordered = daily["date_str"].tolist() + counts_day = daily["count"].tolist() + + avg_per_day = round(len(df_tl) / len(daily), 1) if len(daily) > 0 else 0 + + busiest_row = daily.loc[daily["count"].idxmax()] if len(daily) > 0 and daily["count"].max() > 0 else None + busiest_day = busiest_row["date_str"] if busiest_row is not None else "-" + busiest_cnt = int(busiest_row["count"]) if busiest_row is not None else 0 + + _section_header( + chart_title, + f"Dihitung berdasarkan {chart_label} Β· {filter_label}" + ) + + bar_clrs = [ + "#1d4ed8" if c == busiest_cnt else "#3b6cf7" + for c in counts_day + ] + + fig_bar = go.Figure(data=[ + go.Bar( + x=ordered, + y=counts_day, + marker=dict(color=bar_clrs, line=dict(width=0), opacity=0.88), + width=0.5, + hovertemplate=f"%{{x}}
%{{y:,}} {count_label}", + text=[str(c) for c in counts_day], + textposition="outside", + textfont=dict(size=10, color="#475569"), + cliponaxis=False, + ) + ]) + + fig_bar.update_layout( + height=340, + margin=dict(l=0, r=10, t=30, b=10), + paper_bgcolor="rgba(0,0,0,0)", + plot_bgcolor="rgba(0,0,0,0)", + xaxis=dict( + type="category", + categoryorder="array", + categoryarray=ordered, + tickfont=dict(size=11, color="#334155"), + showgrid=False, + zeroline=False, + showline=False, + fixedrange=True + ), + yaxis=dict( + tickfont=dict(size=10, color="#475569"), + showgrid=True, + gridcolor="rgba(203,213,225,0.8)", + griddash="dot", + gridwidth=1, + zeroline=False, + showline=False, + fixedrange=True + ), + showlegend=False, + hovermode="x unified", + ) + + st.plotly_chart( + fig_bar, + width="stretch", + config={"displayModeBar": False} + ) + + pc1, pc2 = st.columns(2) + + with pc1: + st.markdown(f""" +
+
πŸ“Š
+
+
{avg_label}
+
+ {avg_per_day} {count_label}
+
+
+""", unsafe_allow_html=True) + + with pc2: + st.markdown(f""" +
+
πŸ”₯
+
+
Hari {count_label.title()} Terbanyak
+
+ {busiest_day} + + ({busiest_cnt:,} {count_label}) +
+
+
+""", unsafe_allow_html=True) + + +def _render_nrt_status_banner(sched_ok): + if sched_ok: + bg = "linear-gradient(135deg,#f0fdf4,#dcfce7)" + bdr = "#86efac" + dot = "#16a34a" + title = "🟒 Crawler Service Berjalan sebagai Background Job" + desc = ( + f"Sistem crawler service berjalan di background secara otomatis " + f"setiap {NRT_INTERVAL_MINUTES} menit. " + f"Panel monitoring membaca update realtime berdasarkan crawled_at." + ) + else: + bg = "linear-gradient(135deg,#fef9c3,#fef08a)" + bdr = "#fcd34d" + dot = "#f59e0b" + title = "⚠️ Crawler Service Belum Aktif" + desc = ( + f"Jalankan python3 crawler.py di terminal terpisah " + f"untuk mengaktifkan crawler otomatis setiap " + f"{NRT_INTERVAL_MINUTES} menit." + ) + + st.markdown(f""" +
+
+
+
{title}
+
{desc}
+
+
+""", unsafe_allow_html=True) + + +def _render_monitoring_panel(last_log, last_dt, sched_ok): + now = user_now() + + try: + df_stats = pd.read_sql( + "SELECT crawled_at FROM tweets", + engine + ) + + if not df_stats.empty: + df_stats["crawled_at"] = parse_crawled_dt(df_stats["crawled_at"]) + + total_db = len(df_stats) + interval_start = now - timedelta(minutes=NRT_INTERVAL_MINUTES) + tweet_interval = ( + len(df_stats[df_stats["crawled_at"] >= pd.Timestamp(interval_start)]) + if not df_stats.empty else 0 + ) + latest_crawled = ( + df_stats["crawled_at"].max() + if not df_stats.empty else None + ) + except Exception: + total_db = 0 + tweet_interval = 0 + latest_crawled = None + + last_log_dt = last_dt + + if last_log_dt is None and last_log: + last_log_dt = parse_log_time(last_log.get("timestamp")) + + final_last_dt = to_user_local_datetime(last_log_dt) + + if final_last_dt is None: + final_last_dt = to_user_local_datetime(latest_crawled) + + last_crawl_str = "Belum pernah" + next_crawl_str = "Belum terjadwal" + monitoring_time_str = format_dt(now) + countdown_str = "Belum terjadwal" + + if final_last_dt is not None: + next_dt = final_last_dt + timedelta(minutes=NRT_INTERVAL_MINUTES) + remaining = next_dt - now + + last_crawl_str = format_dt(final_last_dt) + next_crawl_str = format_dt(next_dt) + + if remaining.total_seconds() > 0: + countdown_str = format_countdown(remaining) + elif is_crawler_running(): + countdown_str = "Sedang crawling..." + else: + countdown_str = "Menunggu hasil crawl terbaru..." + + status_text = "Active" if sched_ok else "Nonaktif" + status_color = "#16a34a" if sched_ok else "#ca8a04" + note_text = ( + "βœ… Data berhasil diperbarui" + if tweet_interval > 0 else + "ℹ️ Tidak ada tweet baru pada periode ini" + ) + note_bg = "#f0fdf4" if tweet_interval > 0 else "#f8fafc" + note_border = "#bbf7d0" if tweet_interval > 0 else "#e2e8f0" + note_color = "#166534" if tweet_interval > 0 else "#475569" + + _section_header( + "πŸ“‘ Status Monitoring Sistem", + "Transparansi proses pengambilan data secara near real-time" + ) + + col_a, col_b = st.columns(2) + + with col_a: + st.markdown(f""" +
+
+ βš™οΈ STATUS SISTEM CRAWLING +
+
+ Crawler Status + {status_text} +
+
+ Interval Crawling + {NRT_INTERVAL_MINUTES} menit +
+
+ Last Crawling + {last_crawl_str} +
+
+ Waktu Monitoring + {monitoring_time_str} +
+
+ Next Crawling + ⏰ {next_crawl_str} +
+
+ Countdown + {countdown_str} +
+
+""", unsafe_allow_html=True) + + with col_b: + st.markdown(f""" +
+
+ πŸ“Š RINGKASAN UPDATE TERAKHIR +
+
+ Update terakhir + {last_crawl_str} +
+
+ Waktu Monitoring + {monitoring_time_str} +
+
+ Menuju Crawl Berikutnya + {countdown_str} +
+
+ + Tweet baru dalam {NRT_INTERVAL_MINUTES} menit terakhir + + + {tweet_interval:,} tweet baru + +
+
+ Total dataset terkumpul + {total_db:,} tweet +
+
+ {note_text} +
+
+""", unsafe_allow_html=True) + + +def get_filtered_data(df_all): + now = user_now() + today = now.date() + mode = st.session_state.analysis_mode + + if mode == "realtime": + mode_display = "Tweet Terkini β€” 7 Hari Terakhir" + mode_color = "#16a34a" + mode_icon = "πŸ“‘" + date_col = "created_at" + basis_label = "Berdasarkan tanggal asli tweet" + chart_label = "tanggal asli tweet" + dt_start = datetime.combine( + today - timedelta(days=6), + datetime.min.time() + ) + dt_end = datetime.combine( + today, + datetime.max.time().replace(microsecond=0) + ) + + elif mode == "30days": + mode_display = "30 Hari Terakhir" + mode_color = "#3b6cf7" + mode_icon = "πŸ“…" + date_col = "created_at" + basis_label = "Berdasarkan tanggal asli tweet" + chart_label = "tanggal asli tweet" + dt_start = datetime.combine( + today - timedelta(days=29), + datetime.min.time() + ) + dt_end = datetime.combine( + today, + datetime.max.time().replace(microsecond=0) + ) + + elif mode == "captured": + mode_display = "Tweet Hari Ini" + mode_color = "#0284c7" + mode_icon = "πŸ“†" + date_col = "created_at" + basis_label = "Berdasarkan tanggal asli tweet" + chart_label = "tanggal asli tweet" + dt_start = datetime.combine(today, datetime.min.time()) + dt_end = datetime.combine(now.date(), datetime.max.time().replace(microsecond=0)) + + else: + start_date = st.session_state.get("custom_start_date", now.date()) + end_date = st.session_state.get("custom_end_date", now.date()) + + mode_display = "Periode Historis Pilihan" + mode_color = "#d97706" + mode_icon = "πŸ”" + date_col = "created_at" + basis_label = "Berdasarkan tanggal asli tweet" + chart_label = "tanggal asli tweet" + dt_start = datetime.combine(start_date, datetime.min.time()) + dt_end = datetime.combine(end_date, datetime.max.time().replace(microsecond=0)) + + df_source = df_all.dropna(subset=[date_col]).copy() + + df = df_source[ + (df_source[date_col] >= pd.Timestamp(dt_start)) & + (df_source[date_col] <= pd.Timestamp(dt_end)) + ].copy() + + filter_label = f"{dt_start.strftime('%d/%m/%Y')} s/d {dt_end.strftime('%d/%m/%Y')}" + + return { + "df": df, + "date_col": date_col, + "mode_display": mode_display, + "mode_color": mode_color, + "mode_icon": mode_icon, + "filter_label": filter_label, + "dt_start": dt_start, + "dt_end": dt_end, + "basis_label": basis_label, + "chart_label": chart_label, + } + + +def show(): + today = user_today() + last_7_days = today - timedelta(days=6) + + _render_crawling_styles() + + if "filter_start_date" not in st.session_state: + st.session_state.filter_start_date = last_7_days + + if "filter_end_date" not in st.session_state: + st.session_state.filter_end_date = today + + st.markdown(""" +
+
+
+ πŸ”„ +
+

Ambil Data Twitter

+
+
+""", unsafe_allow_html=True) + + st.markdown(f""" +
+
+ πŸ“– Apa yang dilakukan halaman ini? +
+
+ Sistem menggunakan database historis sebagai data awal, lalu crawler menambahkan data baru + secara berkala setiap {NRT_INTERVAL_MINUTES} menit. + Semua filter tampilan membaca created_at atau tanggal asli tweet; + panel monitoring crawler tetap memakai crawled_at. +
+
+""", unsafe_allow_html=True) + + if "analysis_mode" not in st.session_state: + st.session_state.analysis_mode = "realtime" + + col1, col2, col3, col4 = st.columns(4) + + MODE_CFG = { + "realtime": { + "col": col1, + "key": "btn_realtime", + "label": "πŸ“‘ Terkini (7 Hari)", + "title": "Tweet Terkini", + "icon": "πŸ“‘", + "desc": "Tweet bertanggal asli dalam 7 hari terakhir", + "active_bg": "linear-gradient(135deg,#dcfce7,#bbf7d0)", + "active_border": "#22c55e", + "active_tc": "#14532d", + "icon_bg": "#16a34a", + }, + "30days": { + "col": col2, + "key": "btn_30days", + "label": "πŸ“… 30 Hari Terakhir", + "title": "30 Hari Terakhir", + "icon": "πŸ“…", + "desc": "Tweet bertanggal asli dalam 30 hari terakhir", + "active_bg": "linear-gradient(135deg,#eef2ff,#e0e7ff)", + "active_border": "#3b6cf7", + "active_tc": "#1e3a8a", + "icon_bg": "#3b6cf7", + }, + "captured": { + "col": col3, + "key": "btn_captured", + "label": "πŸ“† Tweet Hari Ini", + "title": "Tweet Hari Ini", + "icon": "πŸ“†", + "desc": "Tweet bertanggal asli hari ini", + "active_bg": "linear-gradient(135deg,#f0f9ff,#dbeafe)", + "active_border": "#0ea5e9", + "active_tc": "#0c4a6e", + "icon_bg": "#0284c7", + }, + "custom": { + "col": col4, + "key": "btn_custom", + "label": "πŸ” Pilih Tanggal", + "title": "Pilih Tanggal", + "icon": "πŸ”", + "desc": "Pilih data historis berdasarkan tanggal asli tweet", + "active_bg": "linear-gradient(135deg,#fef9c3,#fef08a)", + "active_border": "#f59e0b", + "active_tc": "#713f12", + "icon_bg": "#d97706", + }, + } + + for mode_key, cfg in MODE_CFG.items(): + active = st.session_state.analysis_mode == mode_key + + with cfg["col"]: + if st.button( + cfg["label"], + width="stretch", + type="primary" if active else "secondary", + key=cfg["key"] + ): + st.session_state.analysis_mode = mode_key + st.rerun() + + bg = cfg["active_bg"] if active else "#f8fafc" + bdr = cfg["active_border"] if active else "#e2e8f0" + tc = cfg["active_tc"] if active else "#475569" + ic_bg = cfg["icon_bg"] if active else "#e2e8f0" + ic_c = "white" if active else "#64748b" + title_c = cfg["active_tc"] if active else "#0f172a" + bdr_w = "2px" if active else "1.5px" + + st.markdown(f""" +
+
+ {cfg["icon"]}
+
+ {cfg["title"]}
+
{cfg["desc"]}
+
+""", unsafe_allow_html=True) + + st.markdown("
", unsafe_allow_html=True) + + try: + df_all = pd.read_sql( + "SELECT * FROM tweets ORDER BY created_at DESC", + engine + ) + + except Exception as e: + st.error(f"❌ Gagal membaca database: {str(e)}") + return + + if len(df_all) == 0: + st.warning("Belum ada data di database.") + return + + df_all["created_at"] = parse_dt(df_all["created_at"]) + df_all["crawled_at"] = parse_crawled_dt(df_all["crawled_at"]) + + if st.session_state.analysis_mode == "custom": + df_check = df_all.dropna(subset=["created_at"]).copy() + + if not df_check.empty: + min_db = df_check["created_at"].min().date() + max_db = df_check["created_at"].max().date() + + st.markdown(f""" +
+
+ πŸ“… Pilih Rentang Tanggal Tweet Historis
+
+ Data tersedia dari {min_db.strftime('%d/%m/%Y')} + hingga {max_db.strftime('%d/%m/%Y')} +
+
+""", unsafe_allow_html=True) + + with st.container(border=True, key="custom_date_panel"): + c1, c2, c3 = st.columns( + [2, 2, 1], + gap="medium", + vertical_alignment="bottom" + ) + + try: + start_value = pd.to_datetime( + st.session_state.get("custom_start_date", min_db) + ).date() + except Exception: + start_value = min_db + + try: + end_value = pd.to_datetime( + st.session_state.get("custom_end_date", max_db) + ).date() + except Exception: + end_value = max_db + + if start_value < min_db or start_value > max_db: + start_value = min_db + + if end_value < min_db or end_value > max_db: + end_value = max_db + + if end_value < start_value: + end_value = start_value + + with c1: + cs = st.date_input( + "Dari Tanggal", + value=start_value, + min_value=min_db, + max_value=max_db, + key="custom_start_input" + ) + st.session_state.custom_start_date = cs + + with c2: + ce = st.date_input( + "Sampai Tanggal", + value=end_value, + min_value=min_db, + max_value=max_db, + key="custom_end_input" + ) + st.session_state.custom_end_date = ce + + with c3: + if st.button("βœ… Terapkan", type="primary", width="stretch"): + st.success("βœ… Periode diterapkan!") + time.sleep(0.8) + st.rerun() + + if st.session_state.analysis_mode == "captured": + st_autorefresh( + interval=1000, + key="crawler_monitor_refresh" + ) + + logs = get_auto_crawl_logs(5) + last_log = logs[0] if logs else None + sched_ok = is_crawler_service_active(logs) + + last_dt = None + + if last_log: + last_dt = parse_log_time(last_log.get("timestamp")) + + _section_header( + "πŸ€– Ambil Data dengan Bot", + "Panel ini khusus mode Tweet Hari Ini agar status crawler tidak memenuhi tampilan Terkini" + ) + _render_nrt_status_banner(sched_ok) + _captured_section_gap("sm") + _render_monitoring_panel(last_log, last_dt, sched_ok) + crawler_running = is_crawler_running() + + with st.container(border=True, key="bot_control_panel"): + col_status, col_refresh, col_action = st.columns( + [2.6, 1.1, 1.5], + gap="medium", + vertical_alignment="center" + ) + + with col_status: + if crawler_running: + st.info("⏳ Crawler sedang berjalan β€” tombol ambil data dinonaktifkan sementara") + elif sched_ok: + st.success("βœ… Crawler aktif β€” data diperbarui otomatis") + else: + st.info("ℹ️ Jalankan `python3 crawler.py` untuk mengaktifkan crawler") + + with col_refresh: + previous_auto_refresh = st.session_state.get("auto_refresh_ui", False) + auto_refresh = st.toggle( + "πŸ” Refresh Otomatis", + value=previous_auto_refresh, + key="ar_toggle", + help=f"Halaman diperbarui otomatis setiap {NRT_INTERVAL_MINUTES} menit" + ) + st.session_state.auto_refresh_ui = auto_refresh + + if auto_refresh and not previous_auto_refresh: + st.session_state.crawl_history_started_at = datetime.now(timezone.utc) + st.rerun() + + with col_action: + if st.button( + "⏳ Crawler Berjalan" if crawler_running else "πŸ”₯ Ambil Data", + type="secondary" if crawler_running else "primary", + disabled=crawler_running, + width="stretch", + key="btn_manual_crawl_now" + ): + st.session_state.crawl_history_started_at = datetime.now(timezone.utc) + + with st.spinner("⏳ Mengambil tweet terbaru..."): + auto_crawl_job() + + st.success("βœ… Data berhasil diambil!") + st.rerun() + + _captured_section_gap("md") + + if st.session_state.get("auto_refresh_ui"): + st.caption( + "πŸ” Refresh otomatis aktif. Riwayat akan ikut bertambah saat crawler menulis log baru." + ) + _captured_section_gap("sm") + visible_logs = get_visible_crawl_logs(logs) + + if visible_logs: + rows = [] + + for idx, log in enumerate(visible_logs, start=1): + status = log.get("status", "") + total_saved = int(log.get("total_saved") or 0) + error_msg = log.get("error") + + try: + crawl_time = format_dt( + parse_dt(pd.Series([parse_log_time(log.get("timestamp"))])).iloc[0] + ) + except Exception: + crawl_time = log.get("timestamp", "-") or "-" + + if status == "success": + status_label = "βœ… Berhasil" + note = ( + f"{total_saved:,} tweet baru tersimpan" + if total_saved > 0 else + "Crawling berhasil, tidak ada tweet realtime yang cocok" + ) + else: + status_label = "❌ Gagal" + note = error_msg or "Terjadi kesalahan saat crawling" + + rows.append({ + "No": idx, + "Waktu Crawl": crawl_time, + "Status": status_label, + "Tweet Baru": total_saved, + "Keterangan": note, + }) + + _section_header( + "Riwayat Pengambilan Data", + f"Menampilkan {len(rows)} aktivitas crawler terbaru Β· {NRT_INTERVAL_MINUTES} menit/interval" + ) + _captured_section_gap("sm") + + render_standard_table( + pd.DataFrame(rows), + height=300, + min_width=760, + right_align=["Tweet Baru"], + badge_columns=["Status"], + nowrap=["No", "Waktu Crawl", "Status"], + wide_columns=["Keterangan"], + column_widths={ + "No": "56px", + "Waktu Crawl": "160px", + "Status": "138px", + "Tweet Baru": "118px", + "Keterangan": "320px", + }, + ) + else: + st.info("Riwayat akan tampil setelah Anda menekan Ambil Data atau mengaktifkan Refresh Otomatis.") + + _captured_section_gap("lg") + + today_start = datetime.combine(today, datetime.min.time()) + today_end = datetime.combine( + today, + datetime.max.time().replace(microsecond=0) + ) + scrape_tabs = os.getenv("SCRAPE_TABS", "LATEST,TOP") + scrape_tabs_label = " + ".join( + tab.strip().upper() + for tab in scrape_tabs.split(",") + if tab.strip() + ) or "LATEST" + + bot_df = df_all.copy() + + if "crawl_type" in bot_df.columns: + bot_df = bot_df[ + bot_df["crawl_type"].fillna("").str.lower().eq("realtime") + ].copy() + + bot_df = bot_df.dropna(subset=["created_at", "crawled_at"]) + bot_df = bot_df[ + (bot_df["crawled_at"] >= pd.Timestamp(today_start)) & + (bot_df["crawled_at"] <= pd.Timestamp(today_end)) & + (bot_df["created_at"] >= pd.Timestamp(today_start)) & + (bot_df["created_at"] <= pd.Timestamp(today_end)) + ].sort_values("crawled_at", ascending=False) + + bot_date_note = f"tanggal tweet hari ini ({today_start.strftime('%d/%m/%Y')})" + + _section_header( + "πŸ€– Tweet yang Berhasil Diambil Bot", + f"{len(bot_df):,} tweet baru Β· sumber {scrape_tabs_label} Β· duplikat dilewati Β· {bot_date_note}" + ) + _captured_section_gap("sm") + + if bot_df.empty: + st.info("Belum ada tweet baru yang berhasil disimpan bot hari ini.") + + else: + bot_disp = bot_df.head(100).copy() + bot_disp["Tanggal Tweet"] = bot_disp["created_at"].apply(format_dt) + bot_disp["Waktu Bot Ambil"] = bot_disp["crawled_at"].apply(format_dt) + + render_standard_table( + bot_disp[ + [ + "tweet_id", + "text", + "Tanggal Tweet", + "Waktu Bot Ambil", + ] + ].rename( + columns={ + "tweet_id": "ID Tweet", + "text": "Isi Tweet", + } + ), + height=340, + min_width=920, + nowrap=["ID Tweet", "Tanggal Tweet", "Waktu Bot Ambil"], + wide_columns=["Isi Tweet"], + column_widths={ + "ID Tweet": "170px", + "Isi Tweet": "430px", + "Tanggal Tweet": "150px", + "Waktu Bot Ambil": "160px", + }, + ) + + st.caption( + f"Menampilkan {len(bot_disp):,} dari {len(bot_df):,} tweet yang disimpan bot hari ini" + ) + + result = get_filtered_data(df_all) + + df = result["df"] + date_col = result["date_col"] + mode_display = result["mode_display"] + mode_color = result["mode_color"] + mode_icon = result["mode_icon"] + filter_label = result["filter_label"] + dt_start = result["dt_start"] + dt_end = result["dt_end"] + basis_label = result["basis_label"] + chart_label = result["chart_label"] + + st.session_state.filter_start_date = dt_start + st.session_state.filter_end_date = dt_end + st.session_state.filter_label = filter_label + st.session_state.mode_display = mode_display + st.session_state.filter_date_column = date_col + + st.markdown(f""" +
+ {mode_icon} +
+
{mode_display}
+
+ Periode: + {filter_label}
+
+
+""", unsafe_allow_html=True) + + total = len(df) + days = (pd.Timestamp(dt_end).date() - pd.Timestamp(dt_start).date()).days + 1 + earliest = pd.Timestamp(dt_start).strftime("%d/%m/%Y") + latest = pd.Timestamp(dt_end).strftime("%d/%m/%Y") + + _render_metrics(total, days, earliest, latest, filter_label, basis_label) + _captured_section_gap("sm") + + if len(df) == 0: + _section_header( + "πŸ“‹ Daftar Tweet yang Terkumpul", + f"0 tweet Β· {filter_label}" + ) + st.info("Belum ada tweet dengan tanggal asli pada periode ini.") + return + + st.markdown("
", unsafe_allow_html=True) + + _render_charts(df, filter_label, date_col, chart_label, dt_start, dt_end) + + st.markdown("
", unsafe_allow_html=True) + + display_limit = 100 + + _section_header( + "πŸ“‹ Daftar Tweet yang Terkumpul", + f"Data dari filter aktif Β· {filter_label}" + ) + + df_disp = df.sort_values(date_col, ascending=False).head(display_limit).copy() + df_disp["Tanggal Tweet"] = df_disp["created_at"].apply(format_dt) + df_disp["Masuk Database"] = df_disp["crawled_at"].apply(format_dt) + + render_standard_table( + df_disp[ + [ + "tweet_id", + "text", + "Tanggal Tweet", + "Masuk Database" + ] + ].rename( + columns={ + "tweet_id": "ID Tweet", + "text": "Isi Tweet" + } + ), + height=380, + min_width=840, + nowrap=["ID Tweet", "Tanggal Tweet", "Masuk Database"], + wide_columns=["Isi Tweet"], + column_widths={ + "ID Tweet": "180px", + "Isi Tweet": "420px", + "Tanggal Tweet": "150px", + "Masuk Database": "155px", + }, + ) + + st.caption(f"Menampilkan {len(df_disp):,} dari {total:,} tweet") + + st.markdown("
", unsafe_allow_html=True) + + c_dl, c_nav = st.columns(2) + + with c_dl: + st.download_button( + f"πŸ“₯ Unduh Semua Data ({total:,} tweet)", + df.to_csv(index=False).encode("utf-8"), + f"data_twitter_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv", + "text/csv", + width="stretch" + ) + + with c_nav: + if st.button( + "🧹 Lanjut ke Bersihkan Data β†’", + type="primary", + width="stretch", + key="btn_go_preprocessing" + ): + st.session_state.current_page = "preprocessing" + st.rerun() diff --git a/page_modules/preprocessing_page.py b/page_modules/preprocessing_page.py new file mode 100644 index 0000000..9298b31 --- /dev/null +++ b/page_modules/preprocessing_page.py @@ -0,0 +1,700 @@ +import streamlit as st +import pandas as pd +import os +import re +import string +from collections import Counter +import plotly.graph_objects as go +from datetime import datetime, timedelta +from database import engine, get_tweet_count, get_latest_crawl_time +from page_modules.table_utils import render_standard_table +from timezone_utils import ( + parse_dt_with_tz, + parse_dt_with_source_tz, + get_timezone_label, + get_timezone_name, +) + + +def parse_dt(series): + return parse_dt_with_tz( + series, + st.session_state.get("user_timezone", "WIB (UTC+7)") + ) + + +def parse_crawled_dt(series): + return parse_dt_with_source_tz( + series, + st.session_state.get("user_timezone", "WIB (UTC+7)"), + os.getenv("APP_TIMEZONE", "Asia/Makassar") + ) + + +def format_dt(value): + if value is None or pd.isna(value): + return "Belum ada" + + try: + tz_label = get_timezone_label( + st.session_state.get("user_timezone", "WIB (UTC+7)") + ) + return f"{value.strftime('%d/%m/%Y %H:%M')} {tz_label}" + except Exception: + return "Belum ada" + + +def user_today(): + timezone_choice = st.session_state.get("user_timezone", "WIB (UTC+7)") + return pd.Timestamp.now(tz=get_timezone_name(timezone_choice)).date() + + +def _sync_dynamic_period(): + mode = st.session_state.get("analysis_mode") + today = user_today() + + configs = { + "realtime": ( + today - timedelta(days=6), + today, + "Tweet Terkini β€” 7 Hari Terakhir", + ), + "30days": ( + today - timedelta(days=29), + today, + "30 Hari Terakhir", + ), + "captured": ( + today, + today, + "Tweet Hari Ini", + ), + } + + if mode not in configs: + return + + start_day, end_day, mode_display = configs[mode] + dt_start = datetime.combine(start_day, datetime.min.time()) + dt_end = datetime.combine( + end_day, + datetime.max.time().replace(microsecond=0) + ) + + st.session_state.filter_start_date = dt_start + st.session_state.filter_end_date = dt_end + st.session_state.filter_label = ( + f"{dt_start.strftime('%d/%m/%Y')} s/d {dt_end.strftime('%d/%m/%Y')}" + ) + st.session_state.mode_display = mode_display + st.session_state.filter_date_column = "created_at" + + +def _load_stopwords(): + stopword_file = "indonesian-stopwords-complete.txt" + base = set() + + try: + with open(stopword_file, "r", encoding="utf-8") as f: + base = set(f.read().splitlines()) + + for kata in ["tidak", "bukan", "jangan", "kurang", "lebih"]: + base.discard(kata) + + except FileNotFoundError: + base = { + "yang", "dan", "di", "ke", "dari", "ini", "itu", + "dengan", "untuk", "pada", "adalah", "oleh", "ada", + "ya", "akan", "atau", "juga", "sama", "karena", + "jika", "sudah", "telah" + } + + base.update({ + "rt", "amp", "https", "http", "co", "t", + "wkwk", "wkwkwk", "haha", "hehe", + "yg", "dgn", "utk", "dr", "krn", "tp", "jd", "sdh", + "aja", "doang", "banget", "bgt", "nih", "sih", "dong", "deh", + }) + + return base + + +def _load_stemmer(): + try: + from Sastrawi.Stemmer.StemmerFactory import StemmerFactory + return StemmerFactory().create_stemmer() + except Exception: + return None + + +NORMALISASI = { + "gk": "tidak", + "ga": "tidak", + "gak": "tidak", + "nggak": "tidak", + "ngga": "tidak", + "tdk": "tidak", + "tak": "tidak", + "yg": "yang", + "dgn": "dengan", + "utk": "untuk", + "org": "orang", + "krn": "karena", + "dr": "dari", + "tp": "tapi", + "tpi": "tapi", + "sm": "sama", + "jd": "jadi", + "sdh": "sudah", + "blm": "belum", + "emg": "memang", + "emang": "memang", + "gimana": "bagaimana", + "gitu": "begitu", + "gini": "begini", + "bgt": "banget", + "ongkir": "ongkos kirim", + "freeongkir": "gratis ongkir", + "free": "gratis", + "ecommerce": "e commerce", + "komdigi": "komdigi", +} + + +def step1_cleaning(text): + text = str(text).lower() + text = re.sub(r"http\S+|www\S+|https\S+", "", text) + text = re.sub(r"@\w+", "", text) + text = re.sub(r"#", "", text) + text = re.sub(r"\d+", "", text) + text = text.translate( + str.maketrans("", "", string.punctuation) + ) + text = re.sub(r"[^a-zA-Z\s]", "", text) + text = re.sub(r"\s+", " ", text).strip() + return text + + +def step2_normalization(text): + return " ".join( + NORMALISASI.get(word, word) + for word in text.split() + ) + + +def step3_stopword_removal(text, stopwords): + return " ".join( + word + for word in text.split() + if word not in stopwords and len(word) > 2 + ) + + +def step4_stemming(text, stemmer): + if stemmer is None: + return text + + return stemmer.stem(text) + + +def full_preprocessing(text, stopwords, stemmer): + s1 = step1_cleaning(text) + s2 = step2_normalization(s1) + s3 = step3_stopword_removal(s2, stopwords) + s4 = step4_stemming(s3, stemmer) + + return { + "setelah_cleaning": s1, + "setelah_normalisasi": s2, + "setelah_stopword": s3, + "clean_text": s4, + } + + +def _section_header(title, subtitle=""): + """Render consistent section header card""" + sub_html = ( + f'
{subtitle}
' + if subtitle else "" + ) + + st.markdown(f""" +
+
{title}
+ {sub_html} +
+""", unsafe_allow_html=True) + + +def _render_preprocessing_styles(): + st.markdown(""" + +""", unsafe_allow_html=True) + + +def _section_gap(size="md"): + st.markdown( + f'
', + unsafe_allow_html=True + ) + + +def _pill(col, icon, bg, color, dark, label, val, sub): + """Render metric pill card with consistent styling""" + with col: + fs = "1.25rem" if len(str(val)) <= 8 else "1rem" + + st.markdown(f""" +
+
{icon}
+
{label}
+
{val}
+
{sub}
+
+""", unsafe_allow_html=True) + + +def show(): + st.markdown(""" +
+
+
+ 🧹 +
+

Bersihkan Data

+
+
+ """, unsafe_allow_html=True) + + _render_preprocessing_styles() + + st.markdown(""" +
+
+ πŸ“– Apa yang dilakukan halaman ini? +
+
+ Tweet dibersihkan melalui 4 tahap preprocessing: +
+ β‘  Cleaning β€” hapus URL, mention, hashtag, angka, tanda baca +  β†’  + β‘‘ Normalisasi β€” ubah kata tidak baku +  β†’  + β‘’ Stopword Removal β€” hapus kata tidak bermakna +  β†’  + β‘£ Stemming β€” ubah kata ke bentuk dasar. +
+
+ """, unsafe_allow_html=True) + + if "analysis_mode" not in st.session_state: + st.warning("⚠️ Silakan pilih mode tampilan di halaman Ambil Data Twitter terlebih dahulu.") + return + + _sync_dynamic_period() + + start_date = st.session_state.get("filter_start_date") + end_date = st.session_state.get("filter_end_date") + filter_label = st.session_state.get("filter_label", "-") + mode_display = st.session_state.get("mode_display", "-") + + if start_date is None or end_date is None: + st.warning("⚠️ Silakan buka halaman Ambil Data Twitter terlebih dahulu untuk memilih periode.") + return + + mode_meta = { + "realtime": ("#16a34a", "πŸ“‘"), + "30days": ("#3b6cf7", "πŸ“…"), + "captured": ("#0284c7", "πŸ“†"), + "custom": ("#d97706", "πŸ”"), + } + + mode_color, mode_icon = mode_meta.get( + st.session_state.analysis_mode, + ("#3b6cf7", "πŸ“Š") + ) + + st.markdown(f""" +
+ {mode_icon} +
+
{mode_display}
+
+ Periode: {filter_label} +
+
+
+ """, unsafe_allow_html=True) + + try: + df_all = pd.read_sql( + "SELECT * FROM tweets ORDER BY created_at DESC", + engine + ) + + if df_all.empty: + st.warning("⚠️ Belum ada data. Kembali ke halaman Ambil Data Twitter.") + return + + df_all["created_at"] = parse_dt(df_all["created_at"]) + if "crawled_at" in df_all.columns: + df_all["crawled_at"] = parse_crawled_dt(df_all["crawled_at"]) + + except Exception as e: + st.error(f"❌ {e}") + return + + s_dt = pd.Timestamp(start_date) + e_dt = pd.Timestamp(end_date) + + df = df_all[ + (df_all["created_at"] >= s_dt) + & + (df_all["created_at"] <= e_dt) + ].copy() + + if df.empty: + st.warning( + f"⚠️ Tidak ada tweet dengan tanggal asli dalam periode {filter_label}." + ) + return + + total_tweets_in_db = get_tweet_count() + latest_crawl_marker = get_latest_crawl_time() or "no-crawl" + data_marker = (total_tweets_in_db, latest_crawl_marker) + + cache_key = ( + f"pp_{st.session_state.analysis_mode}_" + f"{start_date}_{end_date}_{total_tweets_in_db}_{latest_crawl_marker}" + ) + + # Clear old cache entries + for old_key in list(st.session_state.keys()): + if old_key.startswith("pp_") and old_key != cache_key: + del st.session_state[old_key] + + force_refresh = data_marker != st.session_state.get("_pp_last_data_marker") + + if cache_key not in st.session_state or force_refresh: + stemmer = _load_stemmer() + stopwords = _load_stopwords() + + with st.spinner("🧹 Sedang memproses 4 tahap preprocessing..."): + results = [] + + for _, row in df.iterrows(): + r = full_preprocessing( + row["text"], + stopwords, + stemmer + ) + + r["tweet_id"] = row.get("tweet_id", "") + r["text_asli"] = row["text"] + r["created_at"] = row["created_at"] + r["crawled_at"] = row.get("crawled_at") + + results.append(r) + + df_c = pd.DataFrame(results) + + df_c = df_c[ + df_c["clean_text"].str.strip().str.len() > 0 + ].copy() + + st.session_state[cache_key] = df_c + st.session_state[cache_key + "_stemmer_ok"] = stemmer is not None + st.session_state["_pp_last_data_marker"] = data_marker + + df_c = st.session_state[cache_key] + stemmer_ok = st.session_state.get( + cache_key + "_stemmer_ok", + False + ) + + avg_b = df["text"].astype(str).str.len().mean() + avg_a = df_c["clean_text"].astype(str).str.len().mean() + red = ((avg_b - avg_a) / avg_b * 100) if avg_b > 0 else 0 + rmv = len(df) - len(df_c) + + _section_header( + "πŸ“Œ Ringkasan Preprocessing", + f"Berdasarkan tanggal asli tweet Β· {filter_label}" + ) + + _section_gap("sm") + + c1, c2, c3, c4 = st.columns(4, gap="medium") + + _pill( + c1, "πŸ“Š", "#eef2ff", "#3b6cf7", "#1e3a8a", + "Tweet Siap Dianalisis", f"{len(df_c):,}", + "Setelah 4 tahap preprocessing" + ) + + _pill( + c2, "πŸ“", "#f0fdf4", "#16a34a", "#14532d", + "Panjang Sebelum", f"{avg_b:.0f} karakter", + "Rata-rata per tweet" + ) + + _pill( + c3, "✨", "#fff7ed", "#ea580c", "#7c2d12", + "Panjang Sesudah", f"{avg_a:.0f} karakter", + "Rata-rata per tweet" + ) + + _pill( + c4, "πŸ—‘οΈ", "#fef2f2", "#ef4444", "#7f1d1d", + "Reduksi Teks", f"{red:.1f}%", + f"{rmv} tweet terlalu pendek dibuang" + ) + + _section_gap("lg") + + steps = [ + ( + "#eef2ff", "#3b6cf7", "#1e3a8a", + "β‘  Cleaning", + "Ubah ke huruf kecil Β· Hapus URL, mention, hashtag, angka, tanda baca, karakter non-alfabet" + ), + ( + "#f0fdf4", "#16a34a", "#14532d", + "β‘‘ Normalisasi Kata", + "Ubah singkatan/kata gaul seperti gk β†’ tidak, ongkir β†’ ongkos kirim." + ), + ( + "#fff7ed", "#ea580c", "#7c2d12", + "β‘’ Stopword Removal", + "Hapus kata tidak bermakna, tetapi pertahankan kata negasi seperti tidak dan bukan." + ), + ( + "#fefce8", "#ca8a04", "#713f12", + "β‘£ Stemming", + f"Ubah kata ke bentuk dasar. {'βœ… Aktif' if stemmer_ok else '⚠️ Nonaktif'}" + ), + ] + + cols = st.columns(4, gap="medium") + + for col, (bg, color, dark, title, desc) in zip(cols, steps): + with col: + st.markdown(f""" +
+
{title}
+
+ {desc} +
+
+ """, unsafe_allow_html=True) + + _section_gap("lg") + + _section_header( + "πŸ“‹ Perbandingan Teks per Tahap Preprocessing", + f"{len(df_c):,} tweet Β· {filter_label}" + ) + + if "crawled_at" not in df_c.columns: + df_c = df_c.copy() + df_c["crawled_at"] = pd.NaT + + disp = df_c[ + [ + "tweet_id", + "created_at", + "crawled_at", + "text_asli", + "setelah_cleaning", + "setelah_normalisasi", + "setelah_stopword", + "clean_text", + ] + ].copy() + + disp.columns = [ + "ID Tweet", + "Tanggal Tweet", + "Masuk Database", + "Teks Asli", + "β‘  Setelah Cleaning", + "β‘‘ Setelah Normalisasi", + "β‘’ Setelah Stopword", + "β‘£ Hasil Akhir", + ] + + disp["Tanggal Tweet"] = disp["Tanggal Tweet"].apply(format_dt) + disp["Masuk Database"] = disp["Masuk Database"].apply(format_dt) + + render_standard_table( + disp, + height=350, + min_width=1580, + nowrap=["ID Tweet", "Tanggal Tweet", "Masuk Database"], + wide_columns=[ + "Teks Asli", + "β‘  Setelah Cleaning", + "β‘‘ Setelah Normalisasi", + "β‘’ Setelah Stopword", + "β‘£ Hasil Akhir", + ], + column_widths={ + "ID Tweet": "160px", + "Tanggal Tweet": "170px", + "Masuk Database": "170px", + "Teks Asli": "300px", + "β‘  Setelah Cleaning": "260px", + "β‘‘ Setelah Normalisasi": "260px", + "β‘’ Setelah Stopword": "260px", + "β‘£ Hasil Akhir": "260px", + }, + ) + + _section_gap("lg") + + with st.container(border=True, key="preprocessing_download_panel"): + c1, c2 = st.columns(2, gap="medium", vertical_alignment="bottom") + + with c1: + st.download_button( + "πŸ“₯ Unduh Hasil Preprocessing Lengkap", + df_c.to_csv(index=False).encode("utf-8"), + f"clean_tweet_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv", + "text/csv", + width="stretch" + ) + + with c2: + out2 = df_c[ + [ + "tweet_id", + "text_asli", + "clean_text" + ] + ].copy() + + out2.columns = [ + "tweet_id", + "tweet", + "clean_text" + ] + + st.download_button( + "πŸ“₯ Unduh Teks Bersih Saja", + out2.to_csv(index=False).encode("utf-8"), + f"teks_bersih_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv", + "text/csv", + width="stretch" + ) + + _section_gap("lg") + + _section_header( + "πŸ“Š Kata-Kata yang Paling Sering Muncul", + f"Dari {len(df_c):,} tweet yang sudah bersih" + ) + + all_words = " ".join( + df_c["clean_text"].fillna("") + ).split() + + stop_extra = { + "ongkos", + "kirim", + "gratis", + "komdigi" + } + + filtered_words = [ + w for w in all_words + if len(w) > 2 and w not in stop_extra + ] + + word_freq = Counter( + filtered_words + ).most_common(20) + + if not word_freq: + st.info("⚠️ Kata tidak cukup untuk ditampilkan.") + + else: + words = [w[0] for w in word_freq] + counts = [w[1] for w in word_freq] + + fig = go.Figure(data=[ + go.Bar( + y=words[::-1], + x=counts[::-1], + orientation="h", + marker=dict(color="#3b6cf7"), + text=[str(c) for c in counts[::-1]], + textposition="outside", + hovertemplate="%{y}
Muncul %{x} kali", + ) + ]) + + fig.update_layout( + height=500, + margin=dict(l=0, r=60, t=8, b=8), + paper_bgcolor="rgba(0,0,0,0)", + plot_bgcolor="rgba(0,0,0,0)", + xaxis=dict(showgrid=True, gridcolor="rgba(203,213,225,0.8)"), + yaxis=dict(showgrid=False), + ) + + with st.container(border=True, key="preprocessing_chart_panel"): + st.plotly_chart( + fig, + width="stretch", + config={"displayModeBar": False} + ) + + _section_gap("lg") + + st.session_state["preprocessed_df"] = df_c + + if st.button( + "πŸ“ˆ Lanjut ke Analisis Sentimen β†’", + type="primary", + width="stretch" + ): + st.session_state.current_page = "sentiment" + st.rerun() diff --git a/page_modules/sentiment_page.py b/page_modules/sentiment_page.py new file mode 100644 index 0000000..56c6572 --- /dev/null +++ b/page_modules/sentiment_page.py @@ -0,0 +1,1356 @@ +import streamlit as st +import pandas as pd +import numpy as np +import os +import re +import string +from collections import Counter +from datetime import datetime, timedelta +from html import escape +from database import engine, get_tweet_count, get_latest_crawl_time +from timezone_utils import ( + parse_dt_with_tz, + parse_dt_with_source_tz, + get_timezone_label, + get_timezone_name, +) +import plotly.graph_objects as go +import joblib + +from page_modules.table_utils import render_standard_table + + +model = joblib.load("model_naive_bayes.pkl") +tfidf = joblib.load("tfidf_vectorizer.pkl") + + +def parse_dt(series): + return parse_dt_with_tz( + series, + st.session_state.get("user_timezone", "WIB (UTC+7)") + ) + + +def parse_crawled_dt(series): + return parse_dt_with_source_tz( + series, + st.session_state.get("user_timezone", "WIB (UTC+7)"), + os.getenv("APP_TIMEZONE", "Asia/Makassar") + ) + + +def format_dt(value): + if value is None or pd.isna(value): + return "Belum ada" + + try: + tz_label = get_timezone_label( + st.session_state.get("user_timezone", "WIB (UTC+7)") + ) + return f"{value.strftime('%d/%m/%Y %H:%M')} {tz_label}" + except Exception: + return "Belum ada" + + +def format_now(): + now = parse_dt(pd.Series([datetime.utcnow().isoformat()])).iloc[0] + return format_dt(now) + + +def user_today(): + timezone_choice = st.session_state.get("user_timezone", "WIB (UTC+7)") + return pd.Timestamp.now(tz=get_timezone_name(timezone_choice)).date() + + +def _sync_dynamic_period(): + mode = st.session_state.get("analysis_mode") + today = user_today() + + configs = { + "realtime": ( + today - timedelta(days=6), + today, + "Tweet Terkini β€” 7 Hari Terakhir", + ), + "30days": ( + today - timedelta(days=29), + today, + "30 Hari Terakhir", + ), + "captured": ( + today, + today, + "Tweet Hari Ini", + ), + } + + if mode not in configs: + return + + start_day, end_day, mode_display = configs[mode] + start_day = pd.Timestamp(start_day).date() + end_day = pd.Timestamp(end_day).date() + # Gunakan timestamp yang lebih inklusif untuk end_date + dt_start = datetime.combine(start_day, datetime.min.time()) + dt_end = datetime.combine( + end_day + timedelta(days=1), # Termasuk seluruh hari end_day + datetime.min.time() + ) + + st.session_state.filter_start_date = dt_start + st.session_state.filter_end_date = dt_end + st.session_state.filter_label = ( + f"{dt_start.strftime('%d/%m/%Y')} s/d {end_day.strftime('%d/%m/%Y')}" + ) + st.session_state.mode_display = mode_display + st.session_state.filter_date_column = "created_at" + + +NORMALISASI = { + "gk": "tidak", + "ga": "tidak", + "gak": "tidak", + "nggak": "tidak", + "ngga": "tidak", + "tdk": "tidak", + "tak": "tidak", + "yg": "yang", + "dgn": "dengan", + "utk": "untuk", + "org": "orang", + "krn": "karena", + "dr": "dari", + "tp": "tapi", + "tpi": "tapi", + "sm": "sama", + "jd": "jadi", + "sdh": "sudah", + "blm": "belum", + "emg": "memang", + "emang": "memang", + "gimana": "bagaimana", + "gitu": "begitu", + "gini": "begini", + "bgt": "banget", + "ongkir": "ongkos kirim", + "freeongkir": "gratis ongkir", + "free": "gratis", + "ecommerce": "e commerce", +} + + +def _load_stopwords(): + stopword_file = "indonesian-stopwords-complete.txt" + base = set() + + try: + with open(stopword_file, "r", encoding="utf-8") as f: + base = set(f.read().splitlines()) + + for kata in ["tidak", "bukan", "jangan", "kurang", "lebih"]: + base.discard(kata) + + except FileNotFoundError: + base = { + "yang", "dan", "di", "ke", "dari", "ini", "itu", + "dengan", "untuk", "pada", "adalah", "oleh", "ada", + "ya", "akan", "atau", "juga" + } + + base.update({ + "rt", "amp", "https", "http", "co", "t", + "wkwk", "wkwkwk", "haha", "hehe", + "yg", "dgn", "utk", "dr", "krn", "tp", "jd", "sdh", + "aja", "doang", "banget", "bgt", "nih", "sih", "dong", "deh", + }) + + return base + + +def _get_stemmer(): + try: + from Sastrawi.Stemmer.StemmerFactory import StemmerFactory + return StemmerFactory().create_stemmer() + except Exception: + return None + + +def preprocess(text, stopwords, stemmer): + text = str(text).lower() + text = re.sub(r"http\S+|www\S+|https\S+", "", text) + text = re.sub(r"@\w+", "", text) + text = re.sub(r"#", "", text) + text = re.sub(r"\d+", "", text) + text = text.translate(str.maketrans("", "", string.punctuation)) + text = re.sub(r"[^a-zA-Z\s]", "", text) + text = re.sub(r"\s+", " ", text).strip() + + text = " ".join( + NORMALISASI.get(word, word) + for word in text.split() + ) + + text = " ".join( + word + for word in text.split() + if word not in stopwords and len(word) > 2 + ) + + if stemmer: + text = stemmer.stem(text) + + return text + + +def predict_batch(texts): + vectors = tfidf.transform(texts) + preds = model.predict(vectors) + + confidences = [] + + if hasattr(model, "predict_proba"): + probs = model.predict_proba(vectors) + confidences = probs.max(axis=1) + else: + confidences = np.ones(len(preds)) + + final = [] + + for pred, conf in zip(preds, confidences): + p = str(pred).lower() + + if p == "positif": + sentiment = "Positif" + elif p == "negatif": + sentiment = "Negatif" + elif p == "netral": + sentiment = "Netral" + else: + sentiment = str(pred).capitalize() + + final.append((sentiment, float(conf))) + + return final + + +def _section_header(title, subtitle=""): + """Render consistent section header card""" + sub_html = ( + f'
{subtitle}
' + if subtitle else "" + ) + + st.markdown(f""" +
+
{title}
+ {sub_html} +
+""", unsafe_allow_html=True) + + +def _render_sentiment_styles(): + st.markdown(""" + +""", unsafe_allow_html=True) + + +def _section_gap(size="md"): + st.markdown( + f'
', + unsafe_allow_html=True + ) + + +def _info_chip(label, value, color="#3b6cf7"): + st.markdown(f""" +
+
+
{label}
+
{value}
+
+
+""", unsafe_allow_html=True) + + +def _pill(col, icon, bg, color, dark, label, val, sub): + """Render metric pill card with consistent styling""" + with col: + fs = "1.25rem" if len(str(val)) <= 8 else "1rem" + + st.markdown(f""" +
+
{icon}
+
{label}
+
{val}
+
{sub}
+
+""", unsafe_allow_html=True) + + +def _render_pie_chart(pos_n, neu_n, neg_n, total, filter_label): + _section_header( + "πŸ”΅ Sebaran Sentimen", + f"Periode {filter_label} Β· Diupdate: {format_now()}" + ) + + labels = [] + values = [] + colors = [] + + color_map = { + "Positif": "#16a34a", + "Netral": "#94a3b8", + "Negatif": "#ef4444", + } + + for label, val in [ + ("Positif", pos_n), + ("Netral", neu_n), + ("Negatif", neg_n) + ]: + if val > 0: + labels.append(label) + values.append(val) + colors.append(color_map[label]) + + dominant = max( + [ + ("Positif", pos_n), + ("Netral", neu_n), + ("Negatif", neg_n) + ], + key=lambda x: x[1] + ) + + dom_pct = round( + dominant[1] / total * 100 + ) if total > 0 else 0 + + dom_emoji = { + "Positif": "😊", + "Netral": "😐", + "Negatif": "😞" + }.get(dominant[0], "πŸ“Š") + + fig = go.Figure(data=[ + go.Pie( + labels=labels, + values=values, + hole=0.62, + marker=dict( + colors=colors, + line=dict(color="white", width=4) + ), + textinfo="label+percent", + textfont=dict(size=12), + hovertemplate="%{label}
%{value:,} tweet β€” %{percent}", + direction="clockwise", + sort=False, + ) + ]) + + fig.add_annotation( + text=f"{dom_pct}%
{dom_emoji} {dominant[0]}", + x=0.5, + y=0.5, + showarrow=False, + align="center", + font=dict(size=20, color="#0f172a") + ) + + fig.update_layout( + height=300, + margin=dict(l=10, r=10, t=10, b=10), + showlegend=True, + legend=dict( + orientation="h", + y=-0.08, + x=0.5, + xanchor="center", + font=dict(size=11) + ), + paper_bgcolor="rgba(0,0,0,0)", + ) + + st.plotly_chart( + fig, + width="stretch", + config={"displayModeBar": False} + ) + + return dominant + + +def _render_bar_chart(pos_n, neu_n, neg_n, filter_label, total): + _section_header( + "πŸ“Š Perbandingan Jumlah Tweet per Sentimen", + f"Periode {filter_label} Β· Diupdate: {format_now()}" + ) + + categories = ["Positif 😊", "Netral 😐", "Negatif 😞"] + values = [pos_n, neu_n, neg_n] + colors = ["#16a34a", "#94a3b8", "#ef4444"] + + # Hitung persentase + percentages = [ + (v / total * 100) if total > 0 else 0 + for v in values + ] + + fig = go.Figure() + + for cat, val, color, pct in zip(categories, values, colors, percentages): + fig.add_trace( + go.Bar( + x=[cat], + y=[val], + marker=dict(color=color, opacity=0.88), + text=[f"{val:,}"], + textposition="outside", + textfont=dict(size=14, color="#0f172a"), + width=0.5, + hovertemplate=f"{cat}
{val:,} tweet ({pct:.1f}%)", + ) + ) + + fig.update_layout( + height=320, + margin=dict(l=0, r=0, t=30, b=0), + paper_bgcolor="rgba(0,0,0,0)", + plot_bgcolor="rgba(0,0,0,0)", + showlegend=False, + xaxis=dict(showgrid=False), + yaxis=dict( + showgrid=True, + gridcolor="rgba(226,232,240,0.8)", + griddash="dot", + ), + ) + + st.plotly_chart( + fig, + width="stretch", + config={"displayModeBar": False} + ) + + +def _render_trend_chart(fdf, filter_label, dt_start, dt_end): + _section_header( + "πŸ“… Tren Sentimen dari Hari ke Hari", + f"Periode {filter_label} Β· Diupdate: {format_now()}" + ) + + df_tl = fdf.copy() + df_tl["date"] = pd.to_datetime( + df_tl["created_at"], + errors="coerce" + ).dt.date + + actual_tl = ( + df_tl + .groupby(["date", "sentiment"]) + .size() + .reset_index(name="count") + ) + + date_range = pd.date_range( + pd.Timestamp(dt_start).date(), + pd.Timestamp(dt_end).date(), + freq="D" + ) + + base_dates = pd.DataFrame({"date": date_range.date}) + + fig = go.Figure() + + config = [ + ("Positif", "#16a34a", "rgba(22,163,74,0.08)"), + ("Netral", "#94a3b8", "rgba(148,163,184,0.06)"), + ("Negatif", "#ef4444", "rgba(239,68,68,0.08)"), + ] + + for sent, color, fill in config: + data = base_dates.merge( + actual_tl[actual_tl["sentiment"] == sent][["date", "count"]], + on="date", + how="left" + ) + data["count"] = data["count"].fillna(0).astype(int) + + fig.add_trace( + go.Scatter( + x=data["date"], + y=data["count"], + name=sent, + mode="lines+markers", + line=dict( + color=color, + width=2.5, + shape="spline", + smoothing=0.8 + ), + marker=dict( + size=6, + color="white", + line=dict(color=color, width=2.5) + ), + fill="tozeroy", + fillcolor=fill, + hovertemplate=f"{sent}
%{{x}}: %{{y}} tweet", + ) + ) + + fig.update_layout( + height=290, + margin=dict(l=0, r=0, t=10, b=0), + paper_bgcolor="rgba(0,0,0,0)", + plot_bgcolor="rgba(0,0,0,0)", + legend=dict( + orientation="h", + y=1.1, + x=0, + font=dict(size=11) + ), + xaxis=dict(showgrid=True, gridcolor="rgba(226,232,240,0.6)"), + yaxis=dict(showgrid=True, gridcolor="rgba(226,232,240,0.6)", griddash="dot"), + hovermode="x unified", + ) + + st.plotly_chart( + fig, + width="stretch", + config={"displayModeBar": False} + ) + + +def _render_wordcloud(fdf): + _section_header( + "☁️ Kata-Kata Populer per Sentimen", + "Word Cloud dari tweet yang sudah melalui preprocessing" + ) + + try: + from wordcloud import WordCloud + import matplotlib.pyplot as plt + + stop_wc = { + "ongkos", + "kirim", + "gratis", + "komdigi", + "ongkir" + } + + wc1, wc2, wc3 = st.columns(3, gap="medium") + + cfg = [ + (wc1, "Positif", "Greens", "😊 Positif", "#f0fdf4", "#14532d"), + (wc2, "Netral", "Blues", "😐 Netral", "#f8fafc", "#334155"), + (wc3, "Negatif", "Reds", "😞 Negatif", "#fef2f2", "#7f1d1d"), + ] + + for col, sent, cmap, title, bg, tc in cfg: + with col: + st.markdown(f""" +
+ {title} +
+ """, unsafe_allow_html=True) + + sub = fdf[ + fdf["sentiment"] == sent + ] + + words = [ + w + for w in " ".join( + sub["clean_text"].fillna("") + ).split() + if len(w) > 2 and w not in stop_wc + ] + + if words: + wc = WordCloud( + width=420, + height=260, + background_color="white", + colormap=cmap, + max_words=60, + relative_scaling=0.5, + collocations=False, + ).generate(" ".join(words)) + + fig, ax = plt.subplots(figsize=(5, 3.1)) + ax.imshow(wc, interpolation="bilinear") + ax.axis("off") + plt.tight_layout(pad=0) + + st.pyplot( + fig, + clear_figure=True + ) + + else: + st.info("Data kata tidak cukup") + + except ImportError: + st.warning("Install wordcloud dan matplotlib terlebih dahulu.") + + +def _render_tweet_table(fdf, filter_label): + _section_header( + "πŸ“‹ Tabel Analisis Sentimen", + f"Total {len(fdf):,} tweet Β· {filter_label}" + ) + + with st.container(border=True, key="sentiment_table_controls"): + tf1, tf2, tf3 = st.columns( + [3, 1.6, 1.7], + gap="medium", + vertical_alignment="bottom" + ) + + with tf1: + search = st.text_input( + "Search tweet", + placeholder="Ketik kata kunci di isi tweet...", + key="sentiment_table_search" + ) + + with tf2: + sf = st.selectbox( + "Filter sentimen", + ["Semua", "Positif 😊", "Netral 😐", "Negatif 😞"], + key="sentiment_table_filter" + ) + + with tf3: + sort_by = st.selectbox( + "Urutkan", + ["Terbaru dulu", "Terlama dulu", "Keyakinan tertinggi"], + key="sentiment_table_sort" + ) + + _section_gap("sm") + + tdf = fdf.copy() + + if "crawled_at" not in tdf.columns: + tdf["crawled_at"] = pd.NaT + + if search: + tdf = tdf[ + tdf["text"].str.contains( + search, + case=False, + na=False + ) + ] + + sf_map = { + "Positif 😊": "Positif", + "Netral 😐": "Netral", + "Negatif 😞": "Negatif", + } + + if sf != "Semua": + tdf = tdf[ + tdf["sentiment"] == sf_map.get(sf, sf) + ] + + if sort_by == "Terbaru dulu": + tdf = tdf.sort_values( + "created_at", + ascending=False + ) + + elif sort_by == "Terlama dulu": + tdf = tdf.sort_values( + "created_at", + ascending=True + ) + + else: + tdf = tdf.sort_values( + "confidence", + ascending=False + ) + + out = tdf[ + [ + "created_at", + "crawled_at", + "text", + "clean_text", + "sentiment", + "confidence" + ] + ].copy() + + out["created_at"] = out["created_at"].apply(format_dt) + out["crawled_at"] = out["crawled_at"].apply(format_dt) + + out["confidence"] = out["confidence"].apply( + lambda x: f"{x:.0%}" + ) + + out["sentiment"] = out["sentiment"].map({ + "Positif": "😊 Positif", + "Netral": "😐 Netral", + "Negatif": "😞 Negatif", + }).fillna(out["sentiment"]) + + out.columns = [ + "Tanggal Tweet", + "Masuk Database", + "Tweet Asli", + "Tweet Bersih", + "Sentimen", + "Keyakinan" + ] + + render_standard_table( + out, + height=400, + min_width=1220, + badge_columns=["Sentimen"], + nowrap=["Tanggal Tweet", "Masuk Database", "Sentimen", "Keyakinan"], + wide_columns=["Tweet Asli", "Tweet Bersih"], + column_widths={ + "Tanggal Tweet": "170px", + "Masuk Database": "170px", + "Tweet Asli": "360px", + "Tweet Bersih": "360px", + "Sentimen": "130px", + "Keyakinan": "110px", + }, + ) + + st.caption( + f"Menampilkan {len(tdf):,} tweet" + ) + + return tdf + + +def _render_policy_recommendations(dominant, pos_n, neu_n, neg_n, total, filter_label): + dom_name = dominant[0] + dom_pct = dominant[1] / total * 100 if total else 0 + + _section_header( + "🎯 Rekomendasi Tindakan & Insight Kebijakan", + f"Berdasarkan analisis {total:,} tweet Β· {filter_label}" + ) + + c1, c2, c3, c4 = st.columns(4, gap="medium") + + _pill( + c1, + {"Positif": "😊", "Netral": "😐", "Negatif": "😞"}.get(dom_name, "πŸ“Š"), + {"Positif": "#f0fdf4", "Netral": "#f8fafc", "Negatif": "#fef2f2"}.get(dom_name, "#eef2ff"), + {"Positif": "#16a34a", "Netral": "#64748b", "Negatif": "#ef4444"}.get(dom_name, "#3b6cf7"), + {"Positif": "#14532d", "Netral": "#334155", "Negatif": "#7f1d1d"}.get(dom_name, "#1e3a8a"), + "Sentimen Dominan", + f"{dom_pct:.1f}%", + dom_name + ) + + _pill( + c2, "😊", "#f0fdf4", "#16a34a", "#14532d", + "Positif", f"{pos_n:,}", "Total tweet positif" + ) + + _pill( + c3, "😐", "#f8fafc", "#64748b", "#334155", + "Netral", f"{neu_n:,}", "Total tweet netral" + ) + + _pill( + c4, "😞", "#fef2f2", "#ef4444", "#7f1d1d", + "Negatif", f"{neg_n:,}", "Total tweet negatif" + ) + + _section_gap("sm") + + neg_pct = neg_n / total * 100 if total else 0 + pos_pct = pos_n / total * 100 if total else 0 + neu_pct = neu_n / total * 100 if total else 0 + + rows = [] + + if neg_pct >= 40: + rows.append(( + "πŸ”΄ URGENT", + "Tanggapi Keluhan Publik", + "Sentimen negatif tinggi menunjukkan ketidakpuasan signifikan", + "Buat klarifikasi resmi dan buka ruang dialog publik", + "Humas / Tim Kebijakan" + )) + + if neg_pct >= 20: + rows.append(( + "🟠 TINGGI", + "Tinjau Ulang Kebijakan", + f"Sentimen negatif mencapai {neg_pct:.1f}%", + "Evaluasi poin kebijakan yang paling banyak dikeluhkan", + "Tim Kebijakan" + )) + + if neu_pct >= 30: + rows.append(( + "🟑 SEDANG", + "Tingkatkan Sosialisasi", + f"Sentimen netral {neu_pct:.1f}% menunjukkan banyak publik belum berpihak", + "Perbanyak konten edukatif dan FAQ resmi", + "Tim Komunikasi" + )) + + if pos_pct >= 40: + rows.append(( + "🟒 INFO", + "Pertahankan Momentum Positif", + f"Sentimen positif {pos_pct:.1f}%", + "Perkuat narasi positif dan gunakan kanal resmi secara konsisten", + "Tim Media Sosial" + )) + + rows.append(( + "πŸ”΅ RUTIN", + "Pemantauan Berkelanjutan", + "Opini publik dapat berubah sewaktu-waktu", + "Pantau sentimen harian dan buat laporan berkala", + "Tim Analis Data" + )) + + df_rek = pd.DataFrame( + rows, + columns=[ + "Prioritas", + "Tindakan", + "Dasar Analisis", + "Rekomendasi Konkret", + "Penanggung Jawab" + ] + ) + + priority_color = { + "πŸ”΄ URGENT": ("#fef2f2", "#7f1d1d", "#ef4444"), + "🟠 TINGGI": ("#fff7ed", "#7c2d12", "#ea580c"), + "🟑 SEDANG": ("#fefce8", "#713f12", "#ca8a04"), + "🟒 INFO": ("#f0fdf4", "#14532d", "#16a34a"), + "πŸ”΅ RUTIN": ("#eff6ff", "#1e3a8a", "#3b6cf7"), + } + + for _, row in df_rek.iterrows(): + bg, tc, bc = priority_color.get( + row["Prioritas"], + ("#f8fafc", "#0f172a", "#64748b") + ) + + st.markdown(f""" +
+
+
+ + {row["Prioritas"]} + +
+
+
+ {row["Tindakan"]} +
+
+ πŸ“Œ {row["Dasar Analisis"]} +
+
+ βœ… {row["Rekomendasi Konkret"]} +
+
+
+ + πŸ‘€ {row["Penanggung Jawab"]} + +
+
+
+ """, unsafe_allow_html=True) + + return df_rek + + +def show(): + st.markdown(""" +
+
+
+ πŸ“ˆ +
+

Analisis Sentimen

+
+
+ """, unsafe_allow_html=True) + + _render_sentiment_styles() + + if "analysis_mode" not in st.session_state: + st.warning("⚠️ Silakan pilih mode tampilan di halaman Ambil Data Twitter terlebih dahulu.") + return + + _sync_dynamic_period() + + start_date = st.session_state.get("filter_start_date") + end_date = st.session_state.get("filter_end_date") + filter_label = st.session_state.get("filter_label", "-") + mode_display = st.session_state.get("mode_display", "-") + + if start_date is None or end_date is None: + st.warning("⚠️ Silakan buka halaman Ambil Data Twitter terlebih dahulu.") + return + + mode_meta = { + "realtime": ("#16a34a", "πŸ“‘"), + "30days": ("#3b6cf7", "πŸ“…"), + "captured": ("#0284c7", "πŸ“†"), + "custom": ("#d97706", "πŸ”"), + } + + mode_color, mode_icon = mode_meta.get( + st.session_state.analysis_mode, + ("#3b6cf7", "πŸ“Š") + ) + + st.markdown(f""" +
+ {mode_icon} +
+
{mode_display}
+
+ Periode: {filter_label} +
+
+
+ """, unsafe_allow_html=True) + + try: + df_all = pd.read_sql( + "SELECT * FROM tweets ORDER BY created_at DESC", + engine + ) + + if df_all.empty: + st.warning("⚠️ Belum ada data tweet.") + return + + df_all["created_at"] = parse_dt(df_all["created_at"]) + if "crawled_at" in df_all.columns: + df_all["crawled_at"] = parse_crawled_dt(df_all["crawled_at"]) + + except Exception as e: + st.error(f"❌ Gagal membaca database: {e}") + return + + s_dt = pd.Timestamp(start_date) + e_dt = pd.Timestamp(end_date) + + # Filter dengan lebih toleran - gunakan < untuk end_date karena sudah incremented + df = df_all[ + (df_all["created_at"] >= s_dt) + & + (df_all["created_at"] < e_dt) + ].copy() + + if df.empty: + st.warning( + f"⚠️ Tidak ada tweet dengan tanggal asli dalam periode {filter_label}." + ) + return + + total_tweets_in_db = get_tweet_count() + latest_crawl_marker = get_latest_crawl_time() or "no-crawl" + data_marker = (total_tweets_in_db, latest_crawl_marker) + + cache_key = ( + f"sent_{st.session_state.analysis_mode}_" + f"{start_date}_{end_date}_{total_tweets_in_db}_{latest_crawl_marker}" + ) + + # Clear old cache entries untuk memastikan data selalu fresh + for old_key in list(st.session_state.keys()): + if old_key.startswith("sent_") and old_key != cache_key: + del st.session_state[old_key] + + force_refresh = data_marker != st.session_state.get("_last_sentiment_data_marker") + + if cache_key not in st.session_state or force_refresh: + if force_refresh: + st.info("πŸ”„ Menyegarkan prediksi sentimen dengan data terbaru...") + + with st.spinner("πŸ” Preprocessing & prediksi sentimen seluruh tweet..."): + stopwords = _load_stopwords() + stemmer = _get_stemmer() + + df["clean_text"] = df["text"].apply( + lambda t: preprocess(t, stopwords, stemmer) + ) + + dfc = df[ + df["clean_text"].str.strip().str.len() > 0 + ].copy() + + results = predict_batch( + dfc["clean_text"].tolist() + ) + + if results: + sentiments, confidences = zip(*results) + else: + sentiments, confidences = [], [] + + dfc["sentiment"] = list(sentiments) + dfc["confidence"] = list(confidences) + + st.session_state[cache_key] = dfc + st.session_state["_last_sentiment_data_marker"] = data_marker + + df_s = st.session_state[cache_key] + + _section_header( + "🎯 Filter Kata Kunci", + "Kosongkan untuk melihat semua tweet" + ) + + with st.container(border=True, key="sentiment_keyword_panel"): + kw = st.text_input( + "Kata kunci", + placeholder="Contoh: ongkir, kurir, komdigi...", + key="sentiment_keyword_search" + ) + + st.markdown(f""" +
+ + Mode {escape(str(mode_display))} + + + Periode {escape(str(filter_label))} + +
+""", unsafe_allow_html=True) + + _section_gap("sm") + + fdf = ( + df_s[ + df_s["text"].str.contains( + kw, + case=False, + na=False + ) + ].copy() + if kw else df_s.copy() + ) + + if fdf.empty: + st.warning("⚠️ Tidak ada tweet yang cocok dengan kata kunci tersebut.") + return + + sc = fdf["sentiment"].value_counts() + total = len(fdf) + + pos_n = int(sc.get("Positif", 0)) + neu_n = int(sc.get("Netral", 0)) + neg_n = int(sc.get("Negatif", 0)) + + pos_p = pos_n / total * 100 + neu_p = neu_n / total * 100 + neg_p = neg_n / total * 100 + + _section_header( + "πŸ“Œ Ringkasan Sentimen", + f"Berdasarkan tanggal asli tweet Β· {filter_label}" + ) + + _section_gap("sm") + + c1, c2, c3, c4 = st.columns(4, gap="medium") + + _pill( + c1, "πŸ“Š", "#eef2ff", "#3b6cf7", "#1e3a8a", + "Total Tweet Dianalisis", f"{total:,}", f"Periode {filter_label}" + ) + + _pill( + c2, "😊", "#f0fdf4", "#16a34a", "#14532d", + "Positif", f"{pos_n:,}", f"{pos_p:.1f}% dari total" + ) + + _pill( + c3, "😐", "#f8fafc", "#64748b", "#334155", + "Netral", f"{neu_n:,}", f"{neu_p:.1f}% dari total" + ) + + _pill( + c4, "😞", "#fef2f2", "#ef4444", "#7f1d1d", + "Negatif", f"{neg_n:,}", f"{neg_p:.1f}% dari total" + ) + + _section_gap("lg") + + col_left, col_right = st.columns(2, gap="medium") + + with col_left: + dominant = _render_pie_chart( + pos_n, + neu_n, + neg_n, + total, + filter_label + ) + + with col_right: + _render_bar_chart( + pos_n, + neu_n, + neg_n, + filter_label, + total + ) + + _section_gap("lg") + + _render_trend_chart( + fdf, + filter_label, + start_date, + end_date + ) + + _section_gap("lg") + + _render_wordcloud(fdf) + + _section_gap("lg") + + tdf = _render_tweet_table( + fdf, + filter_label + ) + + _section_gap("lg") + + df_rek = _render_policy_recommendations( + dominant, + pos_n, + neu_n, + neg_n, + total, + filter_label + ) + + _section_gap("lg") + + _section_header("πŸ“₯ Unduh Hasil Analisis") + + with st.container(border=True, key="sentiment_download_panel"): + d1, d2, d3 = st.columns( + 3, + gap="medium", + vertical_alignment="bottom" + ) + + with d1: + st.download_button( + "πŸ“₯ Semua Hasil Prediksi", + fdf.to_csv(index=False).encode("utf-8"), + f"hasil_prediksi_{datetime.now().strftime('%Y%m%d_%H%M')}.csv", + "text/csv", + width="stretch" + ) + + with d2: + summary = pd.DataFrame({ + "Sentimen": ["Positif", "Netral", "Negatif"], + "Jumlah": [pos_n, neu_n, neg_n], + "Persen": [ + f"{pos_p:.2f}%", + f"{neu_p:.2f}%", + f"{neg_p:.2f}%" + ], + }) + + st.download_button( + "πŸ“ˆ Ringkasan Sentimen", + summary.to_csv(index=False).encode("utf-8"), + f"ringkasan_{datetime.now().strftime('%Y%m%d_%H%M')}.csv", + "text/csv", + width="stretch" + ) + + with d3: + st.download_button( + "🎯 Rekomendasi Tindakan", + df_rek.to_csv(index=False).encode("utf-8"), + f"rekomendasi_{datetime.now().strftime('%Y%m%d_%H%M')}.csv", + "text/csv", + width="stretch" + ) diff --git a/page_modules/table_utils.py b/page_modules/table_utils.py new file mode 100644 index 0000000..04a8ee9 --- /dev/null +++ b/page_modules/table_utils.py @@ -0,0 +1,271 @@ +from html import escape + +import pandas as pd +import streamlit as st + + +TABLE_CSS = """ + +""" + + +TEXT_COLUMN_HINTS = ( + "tweet", + "teks", + "isi", + "keterangan", + "bersih", + "clean", + "hasil akhir", +) + +NOWRAP_COLUMN_HINTS = ( + "id", + "no", + "tanggal", + "waktu", + "status", + "sentimen", + "keyakinan", + "database", +) + + +def _plain_value(value): + if value is None: + return "-" + + try: + if pd.isna(value): + return "-" + except (TypeError, ValueError): + pass + + return str(value) + + +def _badge_class(value): + text = value.lower() + + if "negatif" in text or "gagal" in text: + return "sd-badge-red" + + if "positif" in text or "berhasil" in text: + return "sd-badge-green" + + if "netral" in text: + return "sd-badge-slate" + + return "sd-badge-blue" + + +def _is_text_column(column): + name = column.lower() + return any(hint in name for hint in TEXT_COLUMN_HINTS) + + +def _is_nowrap_column(column): + name = column.lower() + return any(hint in name for hint in NOWRAP_COLUMN_HINTS) + + +def render_standard_table( + df, + *, + height=380, + min_width=760, + right_align=None, + nowrap=None, + badge_columns=None, + wide_columns=None, + column_widths=None, +): + if df.empty: + st.info("Tidak ada data untuk ditampilkan.") + return + + right_align = set(right_align or []) + nowrap = set(nowrap or []) + badge_columns = set(badge_columns or []) + wide_columns = set(wide_columns or []) + column_widths = column_widths or {} + + columns = list(df.columns) + + colgroup = "" + if column_widths: + col_tags = [] + + for column in columns: + width = column_widths.get(column) + style = f' style="width:{escape(str(width))};"' if width else "" + col_tags.append(f"") + + colgroup = f"{''.join(col_tags)}" + + header_cells = [] + + for column in columns: + classes = [] + + if column in right_align: + classes.append("sd-table-right") + + header_cells.append( + f'{escape(str(column))}' + ) + + body_rows = [] + + for _, row in df.iterrows(): + cells = [] + + for column in columns: + value = _plain_value(row[column]) + classes = [] + + if column in right_align: + classes.append("sd-table-right") + + if column in nowrap or _is_nowrap_column(str(column)): + classes.append("sd-table-nowrap") + + if column in wide_columns or _is_text_column(str(column)): + classes.append("sd-table-wide") + + class_attr = f' class="{" ".join(classes)}"' if classes else "" + + if column in badge_columns: + badge_class = _badge_class(value) + cell_html = ( + f'' + f"{escape(value)}" + ) + else: + cell_html = escape(value) + + cells.append(f"{cell_html}") + + body_rows.append(f"{''.join(cells)}") + + table_html = f""" +{TABLE_CSS} +
+
+ + {colgroup} + + {''.join(header_cells)} + + + {''.join(body_rows)} + +
+
+
+""" + + st.markdown(table_html, unsafe_allow_html=True) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..ae4f858 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,15 @@ +streamlit +streamlit-autorefresh +pandas +plotly +joblib +scikit-learn +Sastrawi +python-dotenv +sqlalchemy +wordcloud +matplotlib +openpyxl +playwright +pymysql +numpy \ No newline at end of file diff --git a/run_scraper_loop.py b/run_scraper_loop.py new file mode 100644 index 0000000..5aa75ff --- /dev/null +++ b/run_scraper_loop.py @@ -0,0 +1,4 @@ +from crawler import main + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/scraper_worker.py b/scraper_worker.py new file mode 100644 index 0000000..10c5d70 --- /dev/null +++ b/scraper_worker.py @@ -0,0 +1,364 @@ +import os +import glob +import hashlib +import subprocess +import time +from datetime import datetime, timedelta, timezone +from zoneinfo import ZoneInfo + +import pandas as pd +from pandas.errors import EmptyDataError +from dotenv import load_dotenv + +from database import save_tweets, init_db, get_existing_tweet_ids + +load_dotenv() + +AUTH_TOKEN = os.getenv("TWITTER_AUTH_TOKEN") + +QUERY = os.getenv( + "QUERY", + 'komdigi (ongkir OR "gratis ongkir" OR "free ongkir") OR "pembatasan gratis ongkir" OR "gratis ongkir dibatasi"' +) + +SCRAPE_LIMIT = int( + os.getenv("SCRAPE_LIMIT", "50") +) + +RECENT_DAYS = int( + os.getenv("RECENT_DAYS", "2") +) + +APP_TIMEZONE = os.getenv("APP_TIMEZONE", "Asia/Makassar") + +SCRAPE_TABS = [ + tab.strip().upper() + for tab in os.getenv("SCRAPE_TABS", "LATEST,TOP").split(",") + if tab.strip() +] + + +def get_recent_window(): + today = datetime.now(ZoneInfo(APP_TIMEZONE)).date() + since_day = today - timedelta(days=max(RECENT_DAYS - 1, 0)) + until_day = today + timedelta(days=1) + + return since_day, until_day + + +def build_recent_query(): + query_lower = f" {QUERY.lower()}" + + if " since:" in query_lower or " until:" in query_lower: + return QUERY + + since_day, until_day = get_recent_window() + + return f"({QUERY}) since:{since_day.isoformat()} until:{until_day.isoformat()}" + + +def normalize_tweet_date(value): + parsed = pd.to_datetime(value, errors="coerce", utc=True) + + if pd.isna(parsed): + # Selalu gunakan UTC timezone untuk konsistensi + return datetime.now(timezone.utc).isoformat() + + return parsed.isoformat() + + +def is_in_recent_window(value): + parsed = pd.to_datetime(value, errors="coerce", utc=True) + + if pd.isna(parsed): + return False + + try: + local_dt = parsed.tz_convert(APP_TIMEZONE) + except Exception: + local_dt = parsed + + since_day, until_day = get_recent_window() + tweet_day = local_dt.date() + + return since_day <= tweet_day < until_day + + +def stable_fallback_id(row, full_text): + username = str(row.get("username", "") or "unknown").strip() or "unknown" + digest = hashlib.sha1( + full_text.encode("utf-8", errors="ignore") + ).hexdigest()[:16] + + return f"{username}_{digest}" + + +def extract_tweet_id(row, full_text): + for column in ("id_str", "tweet_id", "id"): + value = row.get(column) + + if pd.notna(value) and str(value).strip(): + return str(value).strip() + + return stable_fallback_id(row, full_text) + + +def is_header_like_row(row, tweet_id, full_text): + text = str(full_text or "").strip().lower() + tweet_id_text = str(tweet_id or "").strip().lower() + created_at = str(row.get("created_at", "") or "").strip().lower() + + header_values = { + "full_text", + "text", + "id_str", + "tweet_id", + "created_at", + "conversation_id_str", + } + + return ( + text in header_values + or tweet_id_text in header_values + or created_at == "created_at" + ) + + +def ambil_file_csv_terbaru(min_mtime=None, output_name=None): + if output_name: + output_path = os.path.join("tweets-data", output_name) + root, ext = os.path.splitext(output_path) + files = [ + path for path in ( + output_path, + f"{root}.old{ext}", + ) + if os.path.exists(path) + ] + else: + files = glob.glob("tweets-data/*.csv") + + if not files: + return None + + if min_mtime is not None: + files = [ + path for path in files + if os.path.getmtime(path) >= min_mtime + ] + + if not files: + return None + + non_empty_files = [ + path for path in files + if os.path.getsize(path) > 2 + ] + + if non_empty_files: + files = non_empty_files + + return max( + files, + key=os.path.getmtime + ) + + +def scrape_tab(tab, effective_query, limit): + output_name = f"hasil_{tab.lower()}.csv" + started_at = time.time() - 1 + + command = [ + "npx", + "--yes", + "tweet-harvest@2.6.1", + "-o", + output_name, + "-s", + effective_query, + "--tab", + tab, + "-l", + str(limit), + "--token", + AUTH_TOKEN, + ] + + print(f"Scraping tab {tab}...") + try: + result = subprocess.run(command, capture_output=True, text=True, timeout=180) + except subprocess.TimeoutExpired: + print(f"tweet-harvest tab {tab} timed out after 180 seconds") + raise RuntimeError( + f"tweet-harvest tab {tab} timed out (likely no results atau network issue)" + ) + + if result.returncode != 0: + error_msg = result.stderr if result.stderr else result.stdout + print(f"tweet-harvest stderr: {error_msg}") + raise RuntimeError( + f"tweet-harvest tab {tab} gagal dengan exit code {result.returncode}: {error_msg}" + ) + + latest_file = ambil_file_csv_terbaru(started_at, output_name) + + if latest_file is None: + print(f"CSV baru untuk tab {tab} tidak ditemukan") + return pd.DataFrame() + + # Cek ukuran file sebelum membaca + try: + file_size = os.path.getsize(latest_file) + if file_size <= 2: + print(f"CSV tab {tab} kosong atau tidak valid (ukuran: {file_size} bytes)") + return pd.DataFrame() + except Exception as e: + print(f"Gagal memeriksa ukuran file {latest_file}: {e}") + return pd.DataFrame() + + try: + df = pd.read_csv(latest_file) + except EmptyDataError as e: + print(f"CSV tab {tab} kosong atau tidak valid: {e}") + return pd.DataFrame() + except Exception as e: + print(f"Gagal membaca CSV tab {tab}: {e}") + return pd.DataFrame() + + if df.empty: + print(f"Data tab {tab} kosong") + return pd.DataFrame() + + if "full_text" not in df.columns: + print(f"Kolom full_text tidak ditemukan pada tab {tab}") + print(df.columns.tolist()) + return pd.DataFrame() + + df["_source_tab"] = tab + return df + + +def scrape_once(limit=SCRAPE_LIMIT): + init_db() + + if not AUTH_TOKEN: + raise ValueError( + "TWITTER_AUTH_TOKEN belum diisi di file .env" + ) + + effective_query = build_recent_query() + tabs = SCRAPE_TABS or ["LATEST"] + + print("Scraping tweet realtime...") + print("Query:", effective_query) + print("Tab:", ", ".join(tabs)) + + frames = [] + errors = [] + + for tab in tabs: + try: + tab_df = scrape_tab(tab, effective_query, limit) + except Exception as e: + errors.append(f"{tab}: {e}") + print(f"Tab {tab} gagal:", e) + continue + + if not tab_df.empty: + frames.append(tab_df) + + if not frames: + # Jika tidak ada data baru dari scraper, kembalikan 0 daripada error + # Ini normal jika tidak ada tweet baru yang sesuai kriteria + if errors: + print("Warning - Scrape failed with errors:", " | ".join(errors)) + else: + print("No new data found in twitter search") + return 0 + + df = pd.concat(frames, ignore_index=True) + print(f"Total hasil mentah dari crawler: {len(df)} tweet") + + df["_full_text_clean"] = df["full_text"].fillna("").astype(str) + df = df[df["_full_text_clean"].str.strip().ne("")].copy() + + data = [] + outside_window = 0 + + for _, row in df.iterrows(): + full_text = str(row.get("_full_text_clean", "")) + tweet_id = extract_tweet_id(row, full_text) + + if is_header_like_row(row, tweet_id, full_text): + continue + + created_at = normalize_tweet_date( + row.get("created_at", datetime.now().isoformat()) + ) + + if not is_in_recent_window(created_at): + outside_window += 1 + continue + + data.append({ + "tweet_id": tweet_id, + "text": full_text, + "created_at": created_at, + "crawled_at": datetime.now(timezone.utc).isoformat(), + }) + + if outside_window: + print( + f"{outside_window} tweet dilewati karena di luar rentang realtime" + ) + + before_dedupe = len(data) + deduped = {} + + for tweet in data: + deduped.setdefault(tweet["tweet_id"], tweet) + + data = list(deduped.values()) + duplicate_between_tabs = before_dedupe - len(data) + + if duplicate_between_tabs: + print( + f"{duplicate_between_tabs} tweet duplikat dari LATEST/TOP dilewati" + ) + + if not data: + print("Tidak ada tweet hari ini yang bisa disimpan") + return 0 + + existing_ids = get_existing_tweet_ids( + [tweet["tweet_id"] for tweet in data] + ) + + new_data = [ + tweet for tweet in data + if tweet["tweet_id"] not in existing_ids + ] + + skipped = len(data) - len(new_data) + + if skipped: + print(f"{skipped} tweet dilewati karena sudah ada di database") + + if not new_data: + print("Tidak ada tweet baru untuk disimpan") + return 0 + + saved = save_tweets( + new_data, + "realtime" + ) + + print( + f"{saved} tweet baru berhasil disimpan" + ) + + return saved + + +if __name__ == "__main__": + scrape_once() diff --git a/sentiment_service.py b/sentiment_service.py new file mode 100644 index 0000000..3e776af --- /dev/null +++ b/sentiment_service.py @@ -0,0 +1,39 @@ +import re +import string +import joblib + +model = joblib.load("model_naive_bayes.pkl") +tfidf = joblib.load("tfidf_vectorizer.pkl") + + +def bersihkan_teks(text): + + text = str(text).lower() + + text = re.sub( + r"http\S+|www\S+|@\w+|#|\d+", + "", + text + ) + + text = text.translate( + str.maketrans("", "", string.punctuation) + ) + + text = re.sub(r"\s+", " ", text).strip() + + return text + + +def prediksi_sentimen(list_text): + + clean_texts = [ + bersihkan_teks(text) + for text in list_text + ] + + vectors = tfidf.transform(clean_texts) + + predictions = model.predict(vectors) + + return clean_texts, predictions \ No newline at end of file diff --git a/tfidf_vectorizer.pkl b/tfidf_vectorizer.pkl new file mode 100644 index 0000000000000000000000000000000000000000..52dd4a962cb525cda2dfa9c6dd48ea1b29714b3d GIT binary patch literal 37921 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 ZWRy6lgU42#7V&W#p