Coverage for session_buddy / utils / search / utilities.py: 51.09%
68 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-04 00:43 -0800
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-04 00:43 -0800
1"""Utility functions for advanced search.
3This module provides helper functions for content processing, time parsing,
4and other search-related utilities.
5"""
7from __future__ import annotations
9from contextlib import suppress
10from datetime import UTC, datetime, timedelta
12from session_buddy.session_types import TimeRange
13from session_buddy.utils.regex_patterns import SAFE_PATTERNS
16def extract_technical_terms(content: str) -> list[str]:
17 """Extract technical terms and patterns from content."""
18 terms = []
20 # Programming language detection
21 lang_pattern_names = [
22 "python_code",
23 "javascript_code",
24 "sql_code",
25 "error_keywords",
26 ]
27 lang_mapping = {
28 "python_code": "python",
29 "javascript_code": "javascript",
30 "sql_code": "sql",
31 "error_keywords": "error",
32 }
34 for pattern_name in lang_pattern_names:
35 pattern = SAFE_PATTERNS[pattern_name]
36 if pattern.search(content):
37 terms.append(lang_mapping[pattern_name])
39 # Extract function names
40 func_pattern = SAFE_PATTERNS["function_definition"]
41 func_matches = func_pattern.findall(content)
42 terms.extend([f"function:{func}" for func in func_matches[:5]]) # Limit to 5
44 # Extract class names
45 class_pattern = SAFE_PATTERNS["class_definition"]
46 class_matches = class_pattern.findall(content)
47 terms.extend([f"class:{cls}" for cls in class_matches[:5]])
49 # Extract file extensions
50 ext_pattern = SAFE_PATTERNS["file_extension"]
51 file_matches = ext_pattern.findall(content)
52 terms.extend([f"filetype:{ext}" for ext in set(file_matches[:10])])
54 return terms[:20] # Limit total terms
57def truncate_content(content: str, max_length: int = 500) -> str:
58 """Truncate content to maximum length."""
59 return content[:max_length] + "..." if len(content) > max_length else content
62def ensure_timezone(timestamp: datetime) -> datetime:
63 """Ensure timestamp has timezone information."""
64 return timestamp.replace(tzinfo=UTC) if timestamp.tzinfo is None else timestamp
67def parse_timeframe_single(timeframe: str) -> datetime | None:
68 """Parse timeframe string into datetime."""
69 with suppress(ValueError):
70 if timeframe.endswith("d"): 70 ↛ 73line 70 didn't jump to line 73 because the condition on line 70 was always true
71 days = int(timeframe[:-1])
72 return datetime.now(UTC) - timedelta(days=days)
73 if timeframe.endswith("h"):
74 hours = int(timeframe[:-1])
75 return datetime.now(UTC) - timedelta(hours=hours)
76 if timeframe.endswith("w"):
77 weeks = int(timeframe[:-1])
78 return datetime.now(UTC) - timedelta(weeks=weeks)
79 if timeframe.endswith("m"):
80 months = int(timeframe[:-1])
81 return datetime.now(UTC) - timedelta(days=months * 30)
82 return None
85def parse_timeframe(timeframe: str) -> TimeRange:
86 """Parse timeframe string into TimeRange object.
88 Supports formats like:
89 - '7d' (last 7 days)
90 - '2024-01' (specific month)
91 - '2024' (specific year)
92 - '2024-01-01..2024-01-31' (date range)
93 """
94 # Range format: 'start..end'
95 if ".." in timeframe: 95 ↛ 96line 95 didn't jump to line 96 because the condition on line 95 was never true
96 parts = timeframe.split("..")
97 start = datetime.fromisoformat(parts[0]).replace(tzinfo=UTC)
98 end = datetime.fromisoformat(parts[1]).replace(tzinfo=UTC)
99 return TimeRange(start=start, end=end)
101 # Relative timeframe: '7d', '2w', etc.
102 if timeframe[-1] in "dhwm": 102 ↛ 109line 102 didn't jump to line 109 because the condition on line 102 was always true
103 end = datetime.now(UTC)
104 relative_start: datetime | None = parse_timeframe_single(timeframe)
105 if relative_start: 105 ↛ 109line 105 didn't jump to line 109 because the condition on line 105 was always true
106 return TimeRange(start=relative_start, end=end)
108 # Year only: '2024'
109 if len(timeframe) == 4 and timeframe.isdigit():
110 year = int(timeframe)
111 start = datetime(year, 1, 1, tzinfo=UTC)
112 end = datetime(year + 1, 1, 1, tzinfo=UTC)
113 return TimeRange(start=start, end=end)
115 # Year-month: '2024-01'
116 if len(timeframe) == 7:
117 year, month = map(int, timeframe.split("-"))
118 start = datetime(year, month, 1, tzinfo=UTC)
119 # Calculate next month
120 if month == 12:
121 end = datetime(year + 1, 1, 1, tzinfo=UTC)
122 else:
123 end = datetime(year, month + 1, 1, tzinfo=UTC)
124 return TimeRange(start=start, end=end)
126 # Default: last 7 days
127 end = datetime.now(UTC)
128 start = end - timedelta(days=7)
129 return TimeRange(start=start, end=end)