clapp-pm 1.0.17__py3-none-any.whl → 1.0.18__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
smart_search.py ADDED
@@ -0,0 +1,451 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ smart_search.py - Akıllı Arama Sistemi
4
+
5
+ Bu modül clapp paketlerinde gelişmiş arama özellikleri sağlar:
6
+ - Fuzzy search (bulanık arama)
7
+ - Arama geçmişi
8
+ - Kategori bazlı arama
9
+ - Dil bazlı filtreleme
10
+ - Popülerlik sıralaması
11
+ - Otomatik tamamlama
12
+ """
13
+
14
+ import os
15
+ import json
16
+ import re
17
+ import difflib
18
+ from typing import Dict, List, Tuple, Optional, Any
19
+ from datetime import datetime, timedelta
20
+ from pathlib import Path
21
+ import pickle
22
+
23
+ class SmartSearch:
24
+ """Akıllı arama motoru"""
25
+
26
+ def __init__(self, history_file: Optional[str] = None):
27
+ """
28
+ SmartSearch başlatıcısı
29
+
30
+ Args:
31
+ history_file: Arama geçmişi dosyası
32
+ """
33
+ if history_file is None:
34
+ history_file = os.path.join(os.path.expanduser("~"), ".clapp", "search_history.json")
35
+
36
+ self.history_file = Path(history_file)
37
+ self.history_file.parent.mkdir(parents=True, exist_ok=True)
38
+
39
+ # Arama geçmişi
40
+ self.search_history = self._load_history()
41
+
42
+ # Kategori tanımları
43
+ self.categories = {
44
+ "cli": ["command", "terminal", "console", "cli", "command-line"],
45
+ "gui": ["gui", "interface", "window", "desktop", "graphical"],
46
+ "utility": ["utility", "tool", "helper", "assistant"],
47
+ "development": ["dev", "development", "programming", "code", "script"],
48
+ "game": ["game", "play", "entertainment", "fun"],
49
+ "productivity": ["productivity", "work", "office", "business"],
50
+ "education": ["education", "learn", "study", "tutorial"],
51
+ "multimedia": ["media", "video", "audio", "image", "photo"]
52
+ }
53
+
54
+ def _load_history(self) -> List[Dict[str, Any]]:
55
+ """Arama geçmişini yükler"""
56
+ try:
57
+ if self.history_file.exists():
58
+ with open(self.history_file, 'r', encoding='utf-8') as f:
59
+ return json.load(f)
60
+ except Exception:
61
+ pass
62
+ return []
63
+
64
+ def _save_history(self):
65
+ """Arama geçmişini kaydeder"""
66
+ try:
67
+ with open(self.history_file, 'w', encoding='utf-8') as f:
68
+ json.dump(self.search_history, f, indent=2, ensure_ascii=False)
69
+ except Exception as e:
70
+ print(f"Arama geçmişi kaydetme hatası: {e}")
71
+
72
+ def add_to_history(self, query: str, results_count: int = 0):
73
+ """Arama geçmişine ekler"""
74
+ # Eski kayıtları temizle (30 günden eski)
75
+ cutoff_date = datetime.now() - timedelta(days=30)
76
+ self.search_history = [
77
+ record for record in self.search_history
78
+ if datetime.fromisoformat(record["timestamp"]) > cutoff_date
79
+ ]
80
+
81
+ # Yeni kayıt ekle
82
+ record = {
83
+ "query": query,
84
+ "timestamp": datetime.now().isoformat(),
85
+ "results_count": results_count
86
+ }
87
+
88
+ # Aynı sorgu varsa güncelle
89
+ for existing in self.search_history:
90
+ if existing["query"] == query:
91
+ existing["timestamp"] = record["timestamp"]
92
+ existing["results_count"] = results_count
93
+ self._save_history()
94
+ return
95
+
96
+ self.search_history.append(record)
97
+ self._save_history()
98
+
99
+ def get_search_history(self, limit: int = 10) -> List[str]:
100
+ """Arama geçmişini döndürür"""
101
+ # En son kullanılan sorguları döndür
102
+ sorted_history = sorted(
103
+ self.search_history,
104
+ key=lambda x: datetime.fromisoformat(x["timestamp"]),
105
+ reverse=True
106
+ )
107
+
108
+ return [record["query"] for record in sorted_history[:limit]]
109
+
110
+ def get_popular_searches(self, limit: int = 5) -> List[Tuple[str, int]]:
111
+ """Popüler aramaları döndürür"""
112
+ # Sorgu frekansını hesapla
113
+ query_counts = {}
114
+ for record in self.search_history:
115
+ query = record["query"]
116
+ query_counts[query] = query_counts.get(query, 0) + 1
117
+
118
+ # En popüler olanları döndür
119
+ sorted_queries = sorted(query_counts.items(), key=lambda x: x[1], reverse=True)
120
+ return sorted_queries[:limit]
121
+
122
+ def fuzzy_search(self, query: str, packages: List[Dict[str, Any]], threshold: float = 0.6) -> List[Dict[str, Any]]:
123
+ """
124
+ Bulanık arama yapar
125
+
126
+ Args:
127
+ query: Arama sorgusu
128
+ packages: Paket listesi
129
+ threshold: Eşleşme eşiği (0.0 - 1.0)
130
+
131
+ Returns:
132
+ Eşleşen paketler (skor ile)
133
+ """
134
+ results = []
135
+ query_lower = query.lower()
136
+
137
+ for package in packages:
138
+ score = 0.0
139
+ name = package.get("name", "").lower()
140
+ description = package.get("description", "").lower()
141
+ language = package.get("language", "").lower()
142
+
143
+ # Tam eşleşme kontrolü
144
+ if query_lower == name:
145
+ score = 1.0
146
+ elif query_lower in name:
147
+ score = 0.9
148
+ elif query_lower in description:
149
+ score = 0.7
150
+ elif query_lower == language:
151
+ score = 0.6
152
+ else:
153
+ # Fuzzy matching
154
+ name_ratio = difflib.SequenceMatcher(None, query_lower, name).ratio()
155
+ desc_ratio = difflib.SequenceMatcher(None, query_lower, description).ratio()
156
+
157
+ score = max(name_ratio, desc_ratio * 0.5)
158
+
159
+ if score >= threshold:
160
+ results.append({
161
+ **package,
162
+ "search_score": score
163
+ })
164
+
165
+ # Skora göre sırala
166
+ results.sort(key=lambda x: x["search_score"], reverse=True)
167
+ return results
168
+
169
+ def search_by_category(self, category: str, packages: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
170
+ """
171
+ Kategoriye göre arama yapar
172
+
173
+ Args:
174
+ category: Kategori adı
175
+ packages: Paket listesi
176
+
177
+ Returns:
178
+ Kategoriye uygun paketler
179
+ """
180
+ if category not in self.categories:
181
+ return []
182
+
183
+ category_keywords = self.categories[category]
184
+ results = []
185
+
186
+ for package in packages:
187
+ name = package.get("name", "").lower()
188
+ description = package.get("description", "").lower()
189
+
190
+ # Kategori anahtar kelimelerini kontrol et
191
+ for keyword in category_keywords:
192
+ if keyword in name or keyword in description:
193
+ results.append(package)
194
+ break
195
+
196
+ return results
197
+
198
+ def search_by_language(self, language: str, packages: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
199
+ """
200
+ Dile göre filtreleme yapar
201
+
202
+ Args:
203
+ language: Dil adı (python, lua, vb.)
204
+ packages: Paket listesi
205
+
206
+ Returns:
207
+ Dile uygun paketler
208
+ """
209
+ language_lower = language.lower()
210
+ return [
211
+ package for package in packages
212
+ if package.get("language", "").lower() == language_lower
213
+ ]
214
+
215
+ def advanced_search(self, query: str, packages: List[Dict[str, Any]],
216
+ filters: Optional[Dict[str, Any]] = None) -> List[Dict[str, Any]]:
217
+ """
218
+ Gelişmiş arama yapar
219
+
220
+ Args:
221
+ query: Arama sorgusu
222
+ packages: Paket listesi
223
+ filters: Filtreler (language, category, min_version, vb.)
224
+
225
+ Returns:
226
+ Filtrelenmiş ve sıralanmış paketler
227
+ """
228
+ if filters is None:
229
+ filters = {}
230
+
231
+ # Başlangıçta tüm paketler
232
+ results = packages
233
+
234
+ # Dil filtresi
235
+ if "language" in filters:
236
+ results = self.search_by_language(filters["language"], results)
237
+
238
+ # Kategori filtresi
239
+ if "category" in filters:
240
+ results = self.search_by_category(filters["category"], results)
241
+
242
+ # Versiyon filtresi
243
+ if "min_version" in filters:
244
+ results = self._filter_by_version(results, filters["min_version"], "min")
245
+
246
+ if "max_version" in filters:
247
+ results = self._filter_by_version(results, filters["max_version"], "max")
248
+
249
+ # Fuzzy search
250
+ if query:
251
+ results = self.fuzzy_search(query, results)
252
+
253
+ # Sıralama
254
+ sort_by = filters.get("sort_by", "relevance")
255
+ if sort_by == "name":
256
+ results.sort(key=lambda x: x.get("name", "").lower())
257
+ elif sort_by == "version":
258
+ results.sort(key=lambda x: x.get("version", "0.0.0"), reverse=True)
259
+ elif sort_by == "language":
260
+ results.sort(key=lambda x: x.get("language", "").lower())
261
+
262
+ return results
263
+
264
+ def _filter_by_version(self, packages: List[Dict[str, Any]], version: str, filter_type: str) -> List[Dict[str, Any]]:
265
+ """Versiyona göre filtreleme"""
266
+ try:
267
+ from packaging import version as pkg_version
268
+ target_version = pkg_version.parse(version)
269
+
270
+ filtered_packages = []
271
+ for package in packages:
272
+ package_version = pkg_version.parse(package.get("version", "0.0.0"))
273
+
274
+ if filter_type == "min" and package_version >= target_version:
275
+ filtered_packages.append(package)
276
+ elif filter_type == "max" and package_version <= target_version:
277
+ filtered_packages.append(package)
278
+
279
+ return filtered_packages
280
+ except Exception:
281
+ return packages
282
+
283
+ def get_search_suggestions(self, partial_query: str, packages: List[Dict[str, Any]], limit: int = 5) -> List[str]:
284
+ """
285
+ Arama önerileri verir
286
+
287
+ Args:
288
+ partial_query: Kısmi sorgu
289
+ packages: Paket listesi
290
+ limit: Maksimum öneri sayısı
291
+
292
+ Returns:
293
+ Öneri listesi
294
+ """
295
+ suggestions = set()
296
+ partial_lower = partial_query.lower()
297
+
298
+ # Paket isimlerinden öneriler
299
+ for package in packages:
300
+ name = package.get("name", "")
301
+ if partial_lower in name.lower():
302
+ suggestions.add(name)
303
+
304
+ # Arama geçmişinden öneriler
305
+ for record in self.search_history:
306
+ query = record["query"]
307
+ if partial_lower in query.lower():
308
+ suggestions.add(query)
309
+
310
+ # Kategori önerileri
311
+ for category in self.categories.keys():
312
+ if partial_lower in category.lower():
313
+ suggestions.add(f"category:{category}")
314
+
315
+ # Dil önerileri
316
+ languages = set(package.get("language", "") for package in packages)
317
+ for language in languages:
318
+ if partial_lower in language.lower():
319
+ suggestions.add(f"language:{language}")
320
+
321
+ return list(suggestions)[:limit]
322
+
323
+ def get_search_analytics(self) -> Dict[str, Any]:
324
+ """Arama analitiklerini döndürür"""
325
+ if not self.search_history:
326
+ return {
327
+ "total_searches": 0,
328
+ "unique_queries": 0,
329
+ "most_popular": [],
330
+ "recent_searches": [],
331
+ "search_trends": {}
332
+ }
333
+
334
+ # Toplam arama sayısı
335
+ total_searches = len(self.search_history)
336
+
337
+ # Benzersiz sorgu sayısı
338
+ unique_queries = len(set(record["query"] for record in self.search_history))
339
+
340
+ # En popüler aramalar
341
+ popular_searches = self.get_popular_searches(5)
342
+
343
+ # Son aramalar
344
+ recent_searches = self.get_search_history(5)
345
+
346
+ # Arama trendleri (son 7 gün)
347
+ trends = {}
348
+ cutoff_date = datetime.now() - timedelta(days=7)
349
+
350
+ for record in self.search_history:
351
+ record_date = datetime.fromisoformat(record["timestamp"])
352
+ if record_date > cutoff_date:
353
+ date_str = record_date.strftime("%Y-%m-%d")
354
+ trends[date_str] = trends.get(date_str, 0) + 1
355
+
356
+ return {
357
+ "total_searches": total_searches,
358
+ "unique_queries": unique_queries,
359
+ "most_popular": popular_searches,
360
+ "recent_searches": recent_searches,
361
+ "search_trends": trends
362
+ }
363
+
364
+ class SearchIndex:
365
+ """Arama indeksi oluşturucu"""
366
+
367
+ def __init__(self):
368
+ """SearchIndex başlatıcısı"""
369
+ self.index = {}
370
+
371
+ def build_index(self, packages: List[Dict[str, Any]]):
372
+ """Paketlerden arama indeksi oluşturur"""
373
+ self.index = {}
374
+
375
+ for package in packages:
376
+ name = package.get("name", "").lower()
377
+ description = package.get("description", "").lower()
378
+ language = package.get("language", "").lower()
379
+
380
+ # Kelimeleri ayır
381
+ words = set()
382
+ words.update(name.split())
383
+ words.update(description.split())
384
+ words.add(language)
385
+
386
+ # İndekse ekle
387
+ for word in words:
388
+ if len(word) >= 2: # En az 2 karakter
389
+ if word not in self.index:
390
+ self.index[word] = []
391
+ self.index[word].append(package)
392
+
393
+ def search_index(self, query: str) -> List[Dict[str, Any]]:
394
+ """İndeksten arama yapar"""
395
+ query_words = query.lower().split()
396
+ results = {}
397
+
398
+ for word in query_words:
399
+ if word in self.index:
400
+ for package in self.index[word]:
401
+ package_id = package.get("name", "")
402
+ if package_id not in results:
403
+ results[package_id] = package
404
+ results[package_id]["match_count"] = 0
405
+ results[package_id]["match_count"] += 1
406
+
407
+ # Eşleşme sayısına göre sırala
408
+ sorted_results = sorted(
409
+ results.values(),
410
+ key=lambda x: x["match_count"],
411
+ reverse=True
412
+ )
413
+
414
+ return sorted_results
415
+
416
+ # Yardımcı fonksiyonlar
417
+ def create_smart_search() -> SmartSearch:
418
+ """Varsayılan ayarlarla SmartSearch oluşturur"""
419
+ return SmartSearch()
420
+
421
+ def create_search_index() -> SearchIndex:
422
+ """SearchIndex oluşturur"""
423
+ return SearchIndex()
424
+
425
+ def search_packages(query: str, packages: List[Dict[str, Any]],
426
+ filters: Optional[Dict[str, Any]] = None) -> List[Dict[str, Any]]:
427
+ """Paketlerde arama yapar"""
428
+ searcher = create_smart_search()
429
+ results = searcher.advanced_search(query, packages, filters)
430
+
431
+ # Arama geçmişine ekle
432
+ searcher.add_to_history(query, len(results))
433
+
434
+ return results
435
+
436
+ def get_search_suggestions(partial_query: str, packages: List[Dict[str, Any]]) -> List[str]:
437
+ """Arama önerileri alır"""
438
+ searcher = create_smart_search()
439
+ return searcher.get_search_suggestions(partial_query, packages)
440
+
441
+ def get_search_analytics() -> Dict[str, Any]:
442
+ """Arama analitiklerini alır"""
443
+ searcher = create_smart_search()
444
+ return searcher.get_search_analytics()
445
+
446
+ def clear_search_history():
447
+ """Arama geçmişini temizler"""
448
+ searcher = create_smart_search()
449
+ searcher.search_history = []
450
+ searcher._save_history()
451
+ print("✅ Arama geçmişi temizlendi")
version.py CHANGED
@@ -2,7 +2,7 @@
2
2
  Version information for clapp-pm package.
3
3
  """
4
4
 
5
- __version__ = "1.0.17"
5
+ __version__ = "1.0.18"
6
6
  __author__ = "Melih Burak Memiş"
7
7
  __email__ = "mburakmemiscy@gmail.com"
8
8
  __description__ = "Lightweight cross-language app manager for Python and Lua"