Coverage for src/prosemark/templates/application/use_cases/create_from_template_use_case.py: 100%

68 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-09-30 23:09 +0000

1"""Use case for creating content from templates.""" 

2 

3from typing import Any 

4 

5from prosemark.templates.domain.exceptions.template_exceptions import ( 

6 InvalidPlaceholderError, 

7 InvalidPlaceholderValueError, 

8 PlaceholderProcessingError, 

9 TemplateDirectoryNotFoundError, 

10 TemplateNotFoundError, 

11 TemplateParseError, 

12 TemplateValidationError, 

13 UserCancelledError, 

14) 

15from prosemark.templates.domain.services.template_service import TemplateService 

16 

17 

18class CreateFromTemplateUseCase: 

19 """Use case orchestrating template content creation.""" 

20 

21 def __init__(self, template_service: TemplateService) -> None: 

22 """Initialize use case with template service. 

23 

24 Args: 

25 template_service: Service providing template operations 

26 

27 """ 

28 self._template_service = template_service 

29 

30 @staticmethod 

31 def _raise_missing_placeholder_error(missing_placeholders: list[str]) -> None: 

32 """Raise an error for missing placeholder values. 

33 

34 Args: 

35 missing_placeholders: List of missing placeholder names 

36 

37 Raises: 

38 InvalidPlaceholderValueError: Always raised with context 

39 

40 """ 

41 msg = f'Missing values for required placeholders: {", ".join(missing_placeholders)}' 

42 raise InvalidPlaceholderValueError( 

43 msg, 

44 placeholder_name=missing_placeholders[0], # Use first for error context 

45 ) 

46 

47 def create_single_template( 

48 self, 

49 template_name: str, 

50 placeholder_values: dict[str, str] | None = None, 

51 *, 

52 interactive: bool = True, 

53 ) -> dict[str, Any]: 

54 """Create content from a single template. 

55 

56 Args: 

57 template_name: Name of template to use 

58 placeholder_values: Optional predefined placeholder values 

59 interactive: Whether to prompt user for missing values 

60 

61 Returns: 

62 Dictionary containing generated content and metadata 

63 

64 """ 

65 try: 

66 # Generate content using template service 

67 if interactive: 

68 content = self._template_service.create_from_template(template_name, placeholder_values) 

69 else: 

70 # In non-interactive mode, we must have all required values 

71 template_info = self._template_service.get_template_info(template_name) 

72 required_placeholders = template_info.get('required_placeholders', []) 

73 

74 provided_values = placeholder_values or {} 

75 missing_placeholders = [name for name in required_placeholders if name not in provided_values] 

76 

77 if missing_placeholders: 

78 CreateFromTemplateUseCase._raise_missing_placeholder_error(missing_placeholders) 

79 else: 

80 content = self._template_service.create_from_template(template_name, provided_values) 

81 

82 except ( 

83 TemplateNotFoundError, 

84 InvalidPlaceholderValueError, 

85 TemplateValidationError, 

86 PlaceholderProcessingError, 

87 UserCancelledError, 

88 TemplateParseError, 

89 InvalidPlaceholderError, 

90 ) as e: 

91 return { 

92 'success': False, 

93 'template_name': template_name, 

94 'template_type': 'single', 

95 'error': str(e), 

96 'error_type': type(e).__name__, 

97 } 

98 else: 

99 # Get template metadata 

100 template_info = self._template_service.get_template_info(template_name) 

101 

102 return { 

103 'success': True, 

104 'template_name': template_name, 

105 'template_type': 'single', 

106 'content': content, 

107 'metadata': template_info, 

108 'placeholder_values': placeholder_values or {}, 

109 } 

110 

111 def create_directory_template( 

112 self, 

113 template_directory_name: str, 

114 placeholder_values: dict[str, str] | None = None, 

115 *, 

116 interactive: bool = True, 

117 ) -> dict[str, Any]: 

118 """Create content from a directory template. 

119 

120 Args: 

121 template_directory_name: Name of template directory to use 

122 placeholder_values: Optional predefined placeholder values 

123 interactive: Whether to prompt user for missing values 

124 

125 Returns: 

126 Dictionary containing generated content and metadata 

127 

128 """ 

129 try: 

130 # Generate content using template service 

131 if interactive: 

132 content_map = self._template_service.create_from_directory_template( 

133 template_directory_name, placeholder_values 

134 ) 

135 else: 

136 # In non-interactive mode, we must have all required values 

137 directory_info = self._template_service.get_directory_template_info(template_directory_name) 

138 required_placeholders = directory_info.get('required_placeholders', []) 

139 

140 provided_values = placeholder_values or {} 

141 missing_placeholders = [name for name in required_placeholders if name not in provided_values] 

142 

143 if missing_placeholders: 

144 CreateFromTemplateUseCase._raise_missing_placeholder_error(missing_placeholders) 

145 else: 

146 content_map = self._template_service.create_from_directory_template( 

147 template_directory_name, provided_values 

148 ) 

149 

150 except ( 

151 TemplateDirectoryNotFoundError, 

152 InvalidPlaceholderValueError, 

153 TemplateValidationError, 

154 PlaceholderProcessingError, 

155 UserCancelledError, 

156 TemplateParseError, 

157 InvalidPlaceholderError, 

158 ) as e: 

159 return { 

160 'success': False, 

161 'template_name': template_directory_name, 

162 'template_type': 'directory', 

163 'error': str(e), 

164 'error_type': type(e).__name__, 

165 } 

166 else: 

167 # Get directory metadata 

168 directory_info = self._template_service.get_directory_template_info(template_directory_name) 

169 

170 return { 

171 'success': True, 

172 'template_name': template_directory_name, 

173 'template_type': 'directory', 

174 'content': content_map, 

175 'file_count': len(content_map), 

176 'metadata': directory_info, 

177 'placeholder_values': placeholder_values or {}, 

178 } 

179 

180 def validate_template_before_creation(self, template_name: str) -> dict[str, Any]: 

181 """Validate a template before attempting to create content from it. 

182 

183 Args: 

184 template_name: Name of template to validate 

185 

186 Returns: 

187 Dictionary containing validation results 

188 

189 """ 

190 try: 

191 # Get template info to validate it exists and is valid 

192 template_info = self._template_service.get_template_info(template_name) 

193 

194 except TemplateNotFoundError as e: 

195 return { 

196 'success': False, 

197 'template_name': template_name, 

198 'template_type': 'single', 

199 'valid': False, 

200 'error': str(e), 

201 'error_type': type(e).__name__, 

202 } 

203 else: 

204 # Check if it has placeholders that would require user input 

205 required_placeholders = template_info.get('required_placeholders', []) 

206 optional_placeholders = template_info.get('optional_placeholders', []) 

207 placeholder_count = template_info.get('placeholder_count', 0) 

208 

209 return { 

210 'success': True, 

211 'template_name': template_name, 

212 'template_type': 'single', 

213 'valid': True, 

214 'has_placeholders': placeholder_count > 0, 

215 'required_placeholders': required_placeholders, 

216 'optional_placeholders': optional_placeholders, 

217 'placeholder_count': placeholder_count, 

218 'metadata': template_info, 

219 } 

220 

221 def validate_directory_template_before_creation(self, template_directory_name: str) -> dict[str, Any]: 

222 """Validate a directory template before attempting to create content from it. 

223 

224 Args: 

225 template_directory_name: Name of template directory to validate 

226 

227 Returns: 

228 Dictionary containing validation results 

229 

230 """ 

231 try: 

232 # Get directory info to validate it exists and is valid 

233 directory_info = self._template_service.get_directory_template_info(template_directory_name) 

234 

235 # Check if it has placeholders that would require user input 

236 required_placeholders = directory_info.get('required_placeholders', []) 

237 optional_placeholders = directory_info.get('optional_placeholders', []) 

238 template_count = directory_info.get('template_count', 0) 

239 

240 return { 

241 'success': True, 

242 'template_name': template_directory_name, 

243 'template_type': 'directory', 

244 'valid': True, 

245 'template_count': template_count, 

246 'required_placeholders': required_placeholders, 

247 'optional_placeholders': optional_placeholders, 

248 'shared_placeholders': directory_info.get('shared_placeholders', []), 

249 'metadata': directory_info, 

250 } 

251 

252 except TemplateDirectoryNotFoundError as e: 

253 return { 

254 'success': False, 

255 'template_name': template_directory_name, 

256 'template_type': 'directory', 

257 'valid': False, 

258 'error': str(e), 

259 'error_type': type(e).__name__, 

260 } 

261 

262 def get_template_preview( 

263 self, 

264 template_name: str, 

265 placeholder_values: dict[str, str] | None = None, 

266 ) -> dict[str, Any]: 

267 """Get a preview of what would be generated from a template. 

268 

269 Args: 

270 template_name: Name of template to preview 

271 placeholder_values: Optional placeholder values for preview 

272 

273 Returns: 

274 Dictionary containing preview information 

275 

276 """ 

277 try: 

278 # Get template info 

279 template_info = self._template_service.get_template_info(template_name) 

280 

281 # Identify what placeholders would need values 

282 required_placeholders = template_info.get('required_placeholders', []) 

283 optional_placeholders = template_info.get('optional_placeholders', []) 

284 provided_values = placeholder_values or {} 

285 

286 missing_required = [name for name in required_placeholders if name not in provided_values] 

287 

288 return { 

289 'success': True, 

290 'template_name': template_name, 

291 'template_type': 'single', 

292 'can_generate': len(missing_required) == 0, 

293 'missing_required_placeholders': missing_required, 

294 'provided_placeholders': list(provided_values.keys()), 

295 'all_required_placeholders': required_placeholders, 

296 'all_optional_placeholders': optional_placeholders, 

297 'placeholder_values': provided_values, 

298 'metadata': template_info, 

299 } 

300 

301 except TemplateNotFoundError as e: 

302 return { 

303 'success': False, 

304 'template_name': template_name, 

305 'template_type': 'single', 

306 'error': str(e), 

307 'error_type': type(e).__name__, 

308 }