clapp-pm 1.0.16__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.
- cache_manager.py +376 -0
- {clapp_pm-1.0.16.data → clapp_pm-1.0.18.data}/data/version.json +1 -1
- {clapp_pm-1.0.16.dist-info → clapp_pm-1.0.18.dist-info}/METADATA +1 -1
- {clapp_pm-1.0.16.dist-info → clapp_pm-1.0.18.dist-info}/RECORD +13 -9
- {clapp_pm-1.0.16.dist-info → clapp_pm-1.0.18.dist-info}/top_level.txt +4 -0
- main.py +183 -0
- package_signing.py +370 -0
- smart_search.py +451 -0
- version.py +1 -1
- version_manager.py +351 -0
- {clapp_pm-1.0.16.dist-info → clapp_pm-1.0.18.dist-info}/WHEEL +0 -0
- {clapp_pm-1.0.16.dist-info → clapp_pm-1.0.18.dist-info}/entry_points.txt +0 -0
- {clapp_pm-1.0.16.dist-info → clapp_pm-1.0.18.dist-info}/licenses/LICENSE +0 -0
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