noranisa commited on
Commit
e953a19
·
verified ·
1 Parent(s): 5c6a397

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +21 -171
app.py CHANGED
@@ -1,212 +1,62 @@
1
  import os
2
- import re
3
- import io
4
- import base64
5
  from flask import Flask, render_template, request, redirect, url_for
6
  from serpapi import GoogleSearch
7
  from dotenv import load_dotenv
8
 
9
- # NLP & Data Science Libraries
10
- import nltk
11
- from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer
12
- from sklearn.decomposition import LatentDirichletAllocation
13
- from wordcloud import WordCloud
14
- import matplotlib
15
- matplotlib.use('Agg') # Set non-interactive backend for Matplotlib
16
- import matplotlib.pyplot as plt
17
-
18
- # Import library NLTK
19
- from nltk.corpus import stopwords
20
-
21
- # --- JARING PENGAMAN: Pastikan data NLTK ada ---
22
- # Ini adalah blok "fallback" yang paling penting.
23
- # Ia akan mencoba memuat data. Jika gagal (karena build Docker gagal),
24
- # ia akan mengunduhnya saat runtime.
25
- try:
26
- # Coba akses data. Ini akan memicu LookupError jika tidak ada.
27
- stopwords.words('indonesian')
28
- except LookupError:
29
- # Jika terjadi error, berarti data tidak ada.
30
- print("Data 'stopwords' tidak ditemukan saat startup, mengunduh sekarang...")
31
- # Unduh data. NLTK akan menggunakan ENV NLTK_DATA dari Dockerfile.
32
- nltk.download('stopwords')
33
- print("Download 'stopwords' selesai.")
34
- # ----------------------------------------------------
35
-
36
-
37
- # --- Flask App Setup ---
38
- # Muat environment variable dari file .env (untuk pengembangan lokal)
39
  load_dotenv()
40
 
41
- # Inisialisasi aplikasi Flask
42
  app = Flask(__name__)
43
 
44
- # Ambil API key dari environment variable (Secrets di Hugging Face)
45
  SERPAPI_API_KEY = os.getenv("SERPAPI_API_KEY")
46
 
47
- # Pengecekan keamanan: pastikan API Key ada, jika tidak, aplikasi akan berhenti
48
  if not SERPAPI_API_KEY:
49
- raise ValueError("Environment variable 'SERPAPI_API_KEY' tidak ditemukan. Pastikan sudah diatur di secrets Hugging Face.")
50
-
51
- # --- Helper Functions ---
52
-
53
- def generate_wordcloud(text):
54
- """
55
- Membuat gambar Word Cloud dari gabungan teks dan mengembalikannya
56
- sebagai string base64 yang bisa ditampilkan di HTML.
57
- """
58
- if not text:
59
- return None
60
-
61
- # Ambil daftar stopwords (kata-kata umum) Bahasa Indonesia
62
- stop_words_id = stopwords.words('indonesian')
63
-
64
- # Konfigurasi dan buat objek WordCloud
65
- wordcloud = WordCloud(
66
- width=800,
67
- height=400,
68
- background_color='white',
69
- stopwords=stop_words_id,
70
- colormap='viridis',
71
- max_words=100, # Batasi jumlah kata untuk kejelasan
72
- contour_width=3,
73
- contour_color='steelblue'
74
- ).generate(text)
75
-
76
- # Simpan gambar ke dalam buffer memori, bukan ke file
77
- img = io.BytesIO()
78
- wordcloud.to_image().save(img, 'PNG')
79
- img.seek(0)
80
-
81
- # Encode gambar menjadi string base64 agar bisa disisipkan di HTML
82
- img_b64 = base64.b64encode(img.getvalue()).decode()
83
- return img_b64
84
-
85
- # --- Flask Routes ---
86
 
87
  @app.route('/')
88
  def index():
89
- """
90
- Menampilkan halaman utama dengan form pencarian.
91
- Akan merender file: templates/index.html
92
- """
93
  return render_template('index.html')
94
 
95
  @app.route('/cari', methods=['POST'])
96
  def cari():
97
- """
98
- Endpoint utama yang melakukan semua pekerjaan:
99
- 1. Memproses input dari form.
100
- 2. Memanggil SerpApi.
101
- 3. Melakukan analisis NLP.
102
- 4. Menampilkan halaman hasil dengan visualisasi.
103
- Akan merender file: templates/hasil.html
104
- """
105
- # Ambil data dari form di halaman index.html
106
  topik = request.form.get('topik')
107
  tahun_awal = request.form.get('tahun_awal')
108
  tahun_akhir = request.form.get('tahun_akhir')
109
- jumlah_data = request.form.get('jumlah_data', 10, type=int)
110
 
111
- # Jika topik kosong, kembalikan pengguna ke halaman utama
112
  if not topik:
 
113
  return redirect(url_for('index'))
114
 
115
- # 1. PERSIAPAN DAN PENCARIAN JURNAL DENGAN SERPAPI
116
  params = {
117
  "engine": "google_scholar",
118
- "q": f'"{topik}" pertambangan',
119
- "hl": "id",
120
- "num": jumlah_data,
121
- "as_ylo": tahun_awal,
122
- "as_yhi": tahun_akhir,
123
  "api_key": SERPAPI_API_KEY
124
  }
 
 
125
  search = GoogleSearch(params)
126
  results = search.get_dict()
127
- organic_results = results.get("organic_results", [])
128
-
129
- # 2. PENGUMPULAN DATA UNTUK ANALISIS
130
- # 'corpus' akan menyimpan semua cuplikan (snippet) teks
131
- corpus = []
132
- # 'trend_data' akan menyimpan jumlah publikasi per tahun
133
- trend_data = {}
134
-
135
- for item in organic_results:
136
- snippet = item.get('snippet')
137
- if snippet:
138
- corpus.append(snippet)
139
-
140
- # Ekstraksi tahun dari ringkasan publikasi menggunakan regular expression
141
- year_str = item.get('publication_info', {}).get('summary', '')
142
- year_match = re.search(r'\b(20\d{2})\b', year_str)
143
- if year_match:
144
- year_num = int(year_match.group(1))
145
- trend_data[year_num] = trend_data.get(year_num, 0) + 1
146
-
147
- # Urutkan data tren berdasarkan tahun untuk grafik yang benar
148
- sorted_trend_data = dict(sorted(trend_data.items()))
149
-
150
- # 3. PROSES NLP DAN VISUALISASI
151
- # Inisialisasi variabel hasil analisis untuk dikirim ke template
152
- tfidf_keywords = []
153
- lda_topics = []
154
- wordcloud_image = None
155
 
156
- # Lakukan analisis hanya jika ada data yang berhasil dikumpulkan di corpus
157
- if corpus:
158
- # Gabungkan semua snippet menjadi satu teks besar untuk WordCloud
159
- full_corpus_text = " ".join(corpus)
160
- wordcloud_image = generate_wordcloud(full_corpus_text)
161
-
162
- # Ambil daftar stopwords sekali lagi untuk digunakan di TF-IDF dan LDA
163
- stop_words_id = stopwords.words('indonesian')
164
-
165
- # --- Analisis TF-IDF (Term Frequency-Inverse Document Frequency) ---
166
- try:
167
- tfidf_vectorizer = TfidfVectorizer(max_df=0.85, max_features=50, stop_words=stop_words_id)
168
- tfidf_matrix = tfidf_vectorizer.fit_transform(corpus)
169
- feature_names = tfidf_vectorizer.get_feature_names_out()
170
- total_tfidf_scores = tfidf_matrix.sum(axis=0).tolist()[0]
171
- sorted_indices = sorted(range(len(total_tfidf_scores)), key=lambda k: total_tfidf_scores[k], reverse=True)
172
- tfidf_keywords = [feature_names[i] for i in sorted_indices[:10]]
173
- except ValueError:
174
- # Tangani kasus jika corpus terlalu kecil atau tidak memiliki fitur
175
- tfidf_keywords = ["Data tidak cukup untuk analisis TF-IDF"]
176
-
177
- # --- Analisis LDA (Latent Dirichlet Allocation) untuk menemukan topik ---
178
- try:
179
- count_vectorizer = CountVectorizer(max_df=0.85, max_features=1000, stop_words=stop_words_id)
180
- count_matrix = count_vectorizer.fit_transform(corpus)
181
-
182
- # Tentukan jumlah topik, maksimal 5 atau sesuai jumlah dokumen jika kurang dari 5
183
- num_topics = min(5, len(corpus))
184
- if num_topics > 0:
185
- lda = LatentDirichletAllocation(n_components=num_topics, random_state=42)
186
- lda.fit(count_matrix)
187
-
188
- lda_feature_names = count_vectorizer.get_feature_names_out()
189
- for topic_idx, topic in enumerate(lda.components_):
190
- # Ambil 10 kata teratas untuk setiap topik
191
- top_words_indices = topic.argsort()[:-10 - 1:-1]
192
- top_words = [lda_feature_names[i] for i in top_words_indices]
193
- lda_topics.append({"topic_num": topic_idx + 1, "words": top_words})
194
- except ValueError:
195
- # Tangani kasus jika corpus terlalu kecil
196
- lda_topics = []
197
 
198
- # 4. KIRIM SEMUA DATA YANG DIKUMPULKAN DAN DIANALISIS KE HALAMAN HASIL
199
  return render_template(
200
  'hasil.html',
201
- query=topik,
202
  results=organic_results,
203
- total_results=results.get("search_information", {}).get("total_results", 0),
204
- trend_data=sorted_trend_data,
205
- wordcloud_image=wordcloud_image,
206
- tfidf_keywords=tfidf_keywords,
207
- lda_topics=lda_topics
208
  )
209
 
210
- # Blok ini hanya akan berjalan jika Anda menjalankan `python app.py` di komputer lokal
211
  if __name__ == '__main__':
212
  app.run(debug=True)
 
1
  import os
 
 
 
2
  from flask import Flask, render_template, request, redirect, url_for
3
  from serpapi import GoogleSearch
4
  from dotenv import load_dotenv
5
 
6
+ # Muat environment variable dari file .env
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  load_dotenv()
8
 
 
9
  app = Flask(__name__)
10
 
11
+ # Ambil API key dari environment variable
12
  SERPAPI_API_KEY = os.getenv("SERPAPI_API_KEY")
13
 
14
+ # Pastikan API Key ada
15
  if not SERPAPI_API_KEY:
16
+ raise ValueError("Tidak ada SERPAPI_API_KEY di file .env Anda!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
  @app.route('/')
19
  def index():
20
+ """Menampilkan halaman utama dengan form pencarian."""
 
 
 
21
  return render_template('index.html')
22
 
23
  @app.route('/cari', methods=['POST'])
24
  def cari():
25
+ """Memproses form, memanggil SerpApi, dan menampilkan hasil."""
 
 
 
 
 
 
 
 
26
  topik = request.form.get('topik')
27
  tahun_awal = request.form.get('tahun_awal')
28
  tahun_akhir = request.form.get('tahun_akhir')
29
+ jumlah_data = request.form.get('jumlah_data', 10, type=int) # Default 10 jika kosong
30
 
 
31
  if not topik:
32
+ # Jika topik kosong, kembali ke halaman utama
33
  return redirect(url_for('index'))
34
 
35
+ # Parameter untuk pencarian di Google Scholar menggunakan SerpApi
36
  params = {
37
  "engine": "google_scholar",
38
+ "q": f'"{topik}" pertambangan', # Menggabungkan topik dengan konteks "pertambangan"
39
+ "hl": "id", # Bahasa hasil: Indonesia
40
+ "num": jumlah_data, # Jumlah hasil yang diinginkan
41
+ "as_ylo": tahun_awal, # Tahun Awal (Year Low)
42
+ "as_yhi": tahun_akhir, # Tahun Akhir (Year High)
43
  "api_key": SERPAPI_API_KEY
44
  }
45
+
46
+ # Lakukan pencarian
47
  search = GoogleSearch(params)
48
  results = search.get_dict()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
+ # Ambil hasil organik (jurnal/artikel)
51
+ organic_results = results.get("organic_results", [])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
 
53
+ # Kirim hasil ke template 'hasil.html' untuk ditampilkan
54
  return render_template(
55
  'hasil.html',
 
56
  results=organic_results,
57
+ query=topik,
58
+ total_results=results.get("search_information", {}).get("total_results", 0)
 
 
 
59
  )
60
 
 
61
  if __name__ == '__main__':
62
  app.run(debug=True)