Coverage for src/prosemark/templates/application/use_cases/list_templates_use_case.py: 99%
127 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-10-01 00:05 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-10-01 00:05 +0000
1"""Use case for listing available templates."""
3from typing import Any
5from prosemark.templates.domain.exceptions.template_exceptions import (
6 TemplateDirectoryNotFoundError,
7 TemplateError,
8 TemplateNotFoundError,
9)
10from prosemark.templates.domain.services.template_service import TemplateService
13class ListTemplatesUseCase:
14 """Use case for listing and discovering available templates."""
16 def __init__(self, template_service: TemplateService) -> None:
17 """Initialize use case with template service.
19 Args:
20 template_service: Service providing template operations
22 """
23 self._template_service = template_service
25 def list_all_templates(self) -> dict[str, Any]:
26 """List all available templates (both single and directory).
28 Returns:
29 Dictionary containing all available templates organized by type
31 """
32 try:
33 # Get single templates
34 single_templates = self._template_service.list_templates()
36 # Get directory templates
37 directory_templates = self._template_service.list_template_directories()
39 # Get detailed info for each single template
40 single_template_details = []
41 for template_name in single_templates:
42 try:
43 info = self._template_service.get_template_info(template_name)
44 single_template_details.append(info)
45 except TemplateError:
46 # Skip templates that can't be loaded
47 continue
49 # Get detailed info for each directory template
50 directory_template_details = []
51 for directory_name in directory_templates:
52 try:
53 info = self._template_service.get_directory_template_info(directory_name)
54 directory_template_details.append(info)
55 except TemplateError:
56 # Skip directories that can't be loaded
57 continue
59 return {
60 'success': True,
61 'single_templates': {
62 'count': len(single_template_details),
63 'names': single_templates,
64 'details': single_template_details,
65 },
66 'directory_templates': {
67 'count': len(directory_template_details),
68 'names': directory_templates,
69 'details': directory_template_details,
70 },
71 'total_templates': len(single_templates) + len(directory_templates),
72 'summary': ListTemplatesUseCase._create_templates_summary(
73 single_template_details, directory_template_details
74 ),
75 }
77 except TemplateError as e:
78 return {
79 'success': False,
80 'error': str(e),
81 'error_type': type(e).__name__,
82 }
84 def list_single_templates(self) -> dict[str, Any]:
85 """List only single templates.
87 Returns:
88 Dictionary containing single template information
90 """
91 try:
92 template_names = self._template_service.list_templates()
94 # Get detailed info for each template
95 template_details = []
96 failed_templates = []
98 for template_name in template_names:
99 try:
100 info = self._template_service.get_template_info(template_name)
101 template_details.append(info)
102 except TemplateError as e:
103 failed_templates.append({
104 'name': template_name,
105 'error': str(e),
106 'error_type': type(e).__name__,
107 })
109 return {
110 'success': True,
111 'template_type': 'single',
112 'count': len(template_details),
113 'names': [detail['name'] for detail in template_details],
114 'details': template_details,
115 'failed': failed_templates,
116 'summary': ListTemplatesUseCase._create_single_templates_summary(template_details),
117 }
119 except TemplateError as e:
120 return {
121 'success': False,
122 'template_type': 'single',
123 'error': str(e),
124 'error_type': type(e).__name__,
125 }
127 def list_directory_templates(self) -> dict[str, Any]:
128 """List only directory templates.
130 Returns:
131 Dictionary containing directory template information
133 """
134 try:
135 directory_names = self._template_service.list_template_directories()
137 # Get detailed info for each directory
138 directory_details = []
139 failed_directories = []
141 for directory_name in directory_names:
142 try:
143 info = self._template_service.get_directory_template_info(directory_name)
144 directory_details.append(info)
145 except TemplateError as e:
146 failed_directories.append({
147 'name': directory_name,
148 'error': str(e),
149 'error_type': type(e).__name__,
150 })
152 return {
153 'success': True,
154 'template_type': 'directory',
155 'count': len(directory_details),
156 'names': [detail['name'] for detail in directory_details],
157 'details': directory_details,
158 'failed': failed_directories,
159 'summary': ListTemplatesUseCase._create_directory_templates_summary(directory_details),
160 }
162 except TemplateError as e:
163 return {
164 'success': False,
165 'template_type': 'directory',
166 'error': str(e),
167 'error_type': type(e).__name__,
168 }
170 def search_templates(self, query: str, *, search_in_descriptions: bool = True) -> dict[str, Any]:
171 """Search for templates by name or description.
173 Args:
174 query: Search query string
175 search_in_descriptions: Whether to search in template descriptions
177 Returns:
178 Dictionary containing search results
180 """
181 try:
182 query_lower = query.lower()
184 # Get all templates
185 all_templates_result = self.list_all_templates()
186 if not all_templates_result['success']:
187 return all_templates_result
189 single_templates = all_templates_result['single_templates']['details']
190 directory_templates = all_templates_result['directory_templates']['details']
192 # Search single templates
193 matching_single = [
194 template
195 for template in single_templates
196 if self._template_matches_query(template, query_lower, search_in_descriptions=search_in_descriptions)
197 ]
199 # Search directory templates
200 matching_directory = [
201 template
202 for template in directory_templates
203 if self._template_matches_query(template, query_lower, search_in_descriptions=search_in_descriptions)
204 ]
206 return {
207 'success': True,
208 'query': query,
209 'search_in_descriptions': search_in_descriptions,
210 'single_templates': {
211 'count': len(matching_single),
212 'results': matching_single,
213 },
214 'directory_templates': {
215 'count': len(matching_directory),
216 'results': matching_directory,
217 },
218 'total_matches': len(matching_single) + len(matching_directory),
219 }
221 except TemplateError as e:
222 return {
223 'success': False,
224 'query': query,
225 'error': str(e),
226 'error_type': type(e).__name__,
227 }
229 def get_template_details(self, template_name: str) -> dict[str, Any]:
230 """Get detailed information about a specific template.
232 Args:
233 template_name: Name of template to get details for
235 Returns:
236 Dictionary containing detailed template information
238 """
239 # Try as single template first
240 try:
241 template_info = self._template_service.get_template_info(template_name)
242 except TemplateNotFoundError:
243 pass
244 else:
245 return {
246 'success': True,
247 'template_name': template_name,
248 'template_type': 'single',
249 'found': True,
250 'details': template_info,
251 }
253 # Try as directory template
254 try:
255 directory_info = self._template_service.get_directory_template_info(template_name)
256 except TemplateDirectoryNotFoundError:
257 pass
258 else:
259 return {
260 'success': True,
261 'template_name': template_name,
262 'template_type': 'directory',
263 'found': True,
264 'details': directory_info,
265 }
267 # Template not found in either location
268 return {
269 'success': True,
270 'template_name': template_name,
271 'template_type': 'unknown',
272 'found': False,
273 'error': f"Template '{template_name}' not found as single template or directory template",
274 }
276 def get_templates_with_placeholders(self) -> dict[str, Any]:
277 """List templates that require placeholder values.
279 Returns:
280 Dictionary containing templates with placeholders
282 """
283 try:
284 all_templates_result = self.list_all_templates()
285 if not all_templates_result['success']: 285 ↛ 286line 285 didn't jump to line 286 because the condition on line 285 was never true
286 return all_templates_result
288 single_templates = all_templates_result['single_templates']['details']
289 directory_templates = all_templates_result['directory_templates']['details']
291 # Filter templates with placeholders
292 single_with_placeholders = [t for t in single_templates if t.get('placeholder_count', 0) > 0]
294 directory_with_placeholders = [
295 t
296 for t in directory_templates
297 if len(t.get('required_placeholders', [])) > 0 or len(t.get('optional_placeholders', [])) > 0
298 ]
300 return {
301 'success': True,
302 'single_templates': {
303 'count': len(single_with_placeholders),
304 'templates': single_with_placeholders,
305 },
306 'directory_templates': {
307 'count': len(directory_with_placeholders),
308 'templates': directory_with_placeholders,
309 },
310 'total_with_placeholders': len(single_with_placeholders) + len(directory_with_placeholders),
311 }
313 except TemplateError as e:
314 return {
315 'success': False,
316 'error': str(e),
317 'error_type': type(e).__name__,
318 }
320 @staticmethod
321 def _create_templates_summary(
322 single_templates: list[dict[str, Any]],
323 directory_templates: list[dict[str, Any]],
324 ) -> dict[str, Any]:
325 """Create summary statistics for all templates.
327 Args:
328 single_templates: List of single template details
329 directory_templates: List of directory template details
331 Returns:
332 Summary statistics dictionary
334 """
335 single_summary = ListTemplatesUseCase._create_single_templates_summary(single_templates)
336 directory_summary = ListTemplatesUseCase._create_directory_templates_summary(directory_templates)
338 return {
339 'total_templates': len(single_templates) + len(directory_templates),
340 'single_templates': single_summary,
341 'directory_templates': directory_summary,
342 'templates_with_placeholders': (
343 single_summary['with_placeholders'] + directory_summary['with_placeholders']
344 ),
345 'templates_without_placeholders': (
346 single_summary['without_placeholders'] + directory_summary['without_placeholders']
347 ),
348 }
350 @staticmethod
351 def _create_single_templates_summary(templates: list[dict[str, Any]]) -> dict[str, Any]:
352 """Create summary statistics for single templates.
354 Args:
355 templates: List of single template details
357 Returns:
358 Summary statistics dictionary
360 """
361 if not templates:
362 return {
363 'count': 0,
364 'with_placeholders': 0,
365 'without_placeholders': 0,
366 'total_placeholders': 0,
367 'avg_placeholders_per_template': 0.0,
368 }
370 with_placeholders = sum(1 for t in templates if t.get('placeholder_count', 0) > 0)
371 without_placeholders = len(templates) - with_placeholders
372 total_placeholders = sum(t.get('placeholder_count', 0) for t in templates)
374 return {
375 'count': len(templates),
376 'with_placeholders': with_placeholders,
377 'without_placeholders': without_placeholders,
378 'total_placeholders': total_placeholders,
379 'avg_placeholders_per_template': total_placeholders / len(templates),
380 }
382 @staticmethod
383 def _create_directory_templates_summary(directories: list[dict[str, Any]]) -> dict[str, Any]:
384 """Create summary statistics for directory templates.
386 Args:
387 directories: List of directory template details
389 Returns:
390 Summary statistics dictionary
392 """
393 if not directories:
394 return {
395 'count': 0,
396 'with_placeholders': 0,
397 'without_placeholders': 0,
398 'total_template_files': 0,
399 'avg_files_per_directory': 0.0,
400 }
402 with_placeholders = sum(
403 1
404 for d in directories
405 if len(d.get('required_placeholders', [])) > 0 or len(d.get('optional_placeholders', [])) > 0
406 )
407 without_placeholders = len(directories) - with_placeholders
408 total_files = sum(d.get('template_count', 0) for d in directories)
410 return {
411 'count': len(directories),
412 'with_placeholders': with_placeholders,
413 'without_placeholders': without_placeholders,
414 'total_template_files': total_files,
415 'avg_files_per_directory': total_files / len(directories),
416 }
418 @staticmethod
419 def _template_matches_query(template: dict[str, Any], query_lower: str, *, search_in_descriptions: bool) -> bool:
420 """Check if a template matches the search query.
422 Args:
423 template: Template details dictionary
424 query_lower: Lowercase search query
425 search_in_descriptions: Whether to search in descriptions
427 Returns:
428 True if template matches query
430 """
431 # Search in name
432 if query_lower in template.get('name', '').lower():
433 return True
435 # Search in placeholder names
436 placeholder_names = template.get('required_placeholders', []) + template.get('optional_placeholders', [])
437 for placeholder_name in placeholder_names:
438 if query_lower in placeholder_name.lower():
439 return True
441 # Search in descriptions if enabled
442 if search_in_descriptions:
443 # Search in frontmatter values if available
444 frontmatter = template.get('frontmatter', {})
445 for value in frontmatter.values():
446 if isinstance(value, str) and query_lower in value.lower():
447 return True
449 return False