Files
7000pctAUTO ff295f446a
Some checks failed
CI / test (push) Has been cancelled
Shellhist CI / test (push) Has been cancelled
Shellhist CI / build (push) Has been cancelled
fix: resolve CI type checking issues
- Add return type annotations to __hash__ (-> int) and __eq__ (-> bool) in HistoryEntry
- Add TextIO import and type annotations for file parameters
- Add type ignore comment for fuzzywuzzy import
- Add HistoryEntry import and list type annotations in time_analysis
- Add assert statements for Optional[datetime] timestamps
- Add TypedDict classes for type-safe pattern dictionaries
- Add CommandPattern import and list[CommandPattern] type annotation
- Add -> None return types to all test methods
- Remove unused HistoryEntry import (F401)
2026-01-31 14:19:11 +00:00

97 lines
2.7 KiB
Python

"""Search functionality for shell history."""
from datetime import datetime
from typing import Optional
from fuzzywuzzy import fuzz # type: ignore
from shellhist.core import HistoryEntry, HistoryStore
def fuzzy_search(
store: HistoryStore,
query: str,
threshold: int = 70,
limit: int = 20,
reverse: bool = False,
recent: bool = False,
) -> list[tuple[HistoryEntry, int]]:
"""Search history with fuzzy matching.
Args:
store: HistoryStore to search.
query: Search query string.
threshold: Minimum similarity score (0-100).
limit: Maximum number of results to return.
reverse: If True, sort by recency (newest first).
recent: If True, boost scores for recent commands.
Returns:
List of (HistoryEntry, score) tuples sorted by score.
"""
commands = store.get_unique_commands()
if not commands:
return []
results = []
now = datetime.now()
for command in commands:
score = fuzz.token_sort_ratio(query.lower(), command.lower())
if score >= threshold:
entry = _get_first_entry(store, command)
if entry:
if recent and entry.timestamp:
hours_old = (now - entry.timestamp).total_seconds() / 3600
if hours_old < 24:
score = min(100, score + 10)
results.append((entry, score))
results.sort(key=lambda x: x[1], reverse=not reverse)
return results[:limit]
def _get_first_entry(store: HistoryStore, command: str) -> Optional[HistoryEntry]:
"""Get the first entry for a command from the store."""
for entry in store.entries:
if entry.command == command:
return entry
return None
def rank_by_frequency(
store: HistoryStore,
results: list[tuple[HistoryEntry, int]],
boost_recent: bool = False,
) -> list[tuple[HistoryEntry, int, int]]:
"""Rank search results by frequency.
Args:
store: HistoryStore for frequency lookups.
results: List of (HistoryEntry, score) tuples.
boost_recent: If True, boost scores for recent commands.
Returns:
List of (HistoryEntry, score, frequency) tuples.
"""
ranked = []
now = datetime.now()
for entry, score in results:
freq = store.get_frequency(entry.command)
if boost_recent and entry.timestamp:
hours_old = (now - entry.timestamp).total_seconds() / 3600
if hours_old < 24:
freq = freq * 2
ranked.append((entry, score, freq))
ranked.sort(key=lambda x: (x[1], x[2]), reverse=True)
return ranked