mathhook_core/educational/enhanced_steps/
formatting.rs1use crate::core::Expression;
4use crate::formatter::latex::LaTeXFormatter;
5use crate::formatter::{FormattingError, MathLanguage};
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9#[derive(Debug, Clone)]
11pub struct FormatContext {
12 pub target_format: MathLanguage,
13 pub include_intermediate_steps: bool,
14 pub verbosity_level: u8,
15}
16
17impl Default for FormatContext {
18 fn default() -> Self {
19 Self {
20 target_format: MathLanguage::default(),
21 include_intermediate_steps: true,
22 verbosity_level: 3,
23 }
24 }
25}
26
27impl FormatContext {
28 pub fn format_expression(&self, expr: &Expression) -> Result<String, FormattingError> {
30 match self.target_format {
31 MathLanguage::LaTeX => expr.to_latex(None),
32 MathLanguage::Wolfram => Ok(expr.to_string()),
33 MathLanguage::Simple | MathLanguage::Human => Ok(expr.to_string()),
34 MathLanguage::Json => {
35 serde_json::to_string(expr).map_err(|e| FormattingError::SerializationError {
36 message: e.to_string(),
37 })
38 }
39 MathLanguage::Markdown => Ok(expr.to_string()),
40 }
41 }
42
43 pub fn format_expression_safe(&self, expr: &Expression) -> String {
45 self.format_expression(expr)
46 .unwrap_or_else(|e| format!("{{error: {}}}", e))
47 }
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct PresentationHints {
53 pub color_theme: String,
54 pub importance: u8,
55 pub animation: String,
56 pub layout: String,
57 pub interactive_elements: Vec<String>,
58}
59
60impl Default for PresentationHints {
61 fn default() -> Self {
62 Self {
63 color_theme: "blue".to_owned(),
64 importance: 3,
65 animation: "fade-in".to_owned(),
66 layout: "standard".to_owned(),
67 interactive_elements: Vec::new(),
68 }
69 }
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct EnhancedStepExplanation {
75 pub steps: Vec<super::EnhancedStep>,
76 pub metadata: ExplanationMetadata,
77 pub summary: ExplanationSummary,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct ExplanationMetadata {
83 pub step_count: usize,
84 pub difficulty_level: u8,
85 pub topic: String,
86 pub method: String,
87 pub estimated_time: u8,
88 pub prerequisites: Vec<String>,
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct ExplanationSummary {
94 pub problem: String,
95 pub approach: String,
96 pub answer: String,
97 pub key_insights: Vec<String>,
98 pub next_steps: Vec<String>,
99}
100
101impl EnhancedStepExplanation {
102 pub fn new(steps: Vec<super::EnhancedStep>) -> Self {
104 let metadata = ExplanationMetadata {
105 step_count: steps.len(),
106 difficulty_level: Self::calculate_difficulty(&steps),
107 topic: Self::determine_topic(&steps),
108 method: Self::determine_method(&steps),
109 estimated_time: Self::estimate_time(&steps),
110 prerequisites: Self::determine_prerequisites(&steps),
111 };
112
113 let summary = ExplanationSummary {
114 problem: Self::extract_problem(&steps),
115 approach: Self::extract_approach(&steps),
116 answer: Self::extract_answer(&steps),
117 key_insights: Self::extract_insights(&steps),
118 next_steps: Self::suggest_next_steps(&steps),
119 };
120
121 Self {
122 steps,
123 metadata,
124 summary,
125 }
126 }
127
128 pub fn to_json(&self) -> Result<String, serde_json::Error> {
130 serde_json::to_string_pretty(self)
131 }
132
133 pub fn to_human_text(&self) -> String {
135 let mut text = String::new();
136 text.push_str(&format!("Problem: {}\n\n", self.summary.problem));
137
138 for (i, step) in self.steps.iter().enumerate() {
139 text.push_str(&format!(
140 "Step {}: {}\n{}\n\n",
141 i + 1,
142 step.title,
143 step.human_message
144 ));
145 }
146
147 text.push_str(&format!("Answer: {}\n", self.summary.answer));
148 text
149 }
150
151 pub fn to_api_data(&self) -> HashMap<String, serde_json::Value> {
153 let mut data = HashMap::new();
154
155 data.insert(
156 "metadata".to_owned(),
157 serde_json::to_value(&self.metadata).unwrap(),
158 );
159 data.insert(
160 "summary".to_owned(),
161 serde_json::to_value(&self.summary).unwrap(),
162 );
163
164 let step_data: Vec<serde_json::Value> = self
165 .steps
166 .iter()
167 .map(|step| serde_json::to_value(&step.api_data).unwrap())
168 .collect();
169 data.insert("steps".to_owned(), serde_json::Value::Array(step_data));
170
171 data
172 }
173
174 fn calculate_difficulty(steps: &[super::EnhancedStep]) -> u8 {
175 match steps.len() {
176 1..=3 => 2,
177 4..=6 => 4,
178 7..=10 => 6,
179 _ => 8,
180 }
181 }
182
183 fn determine_topic(steps: &[super::EnhancedStep]) -> String {
184 if let Some(first_step) = steps.first() {
185 first_step.api_data.category.clone()
186 } else {
187 "unknown".to_owned()
188 }
189 }
190
191 fn determine_method(steps: &[super::EnhancedStep]) -> String {
192 if steps
193 .iter()
194 .any(|s| s.api_data.operation.contains("quadratic_formula"))
195 {
196 "Quadratic Formula".to_owned()
197 } else if steps
198 .iter()
199 .any(|s| s.api_data.operation.contains("isolation"))
200 {
201 "Variable Isolation".to_owned()
202 } else {
203 "General Algebraic Method".to_owned()
204 }
205 }
206
207 fn estimate_time(steps: &[super::EnhancedStep]) -> u8 {
208 (steps.len() as u8).saturating_mul(2).min(30)
209 }
210
211 fn determine_prerequisites(steps: &[super::EnhancedStep]) -> Vec<String> {
212 let mut prereqs = vec!["Basic Algebra".to_owned()];
213
214 if steps
215 .iter()
216 .any(|s| s.api_data.category == "quadratic_equation")
217 {
218 prereqs.push("Quadratic Equations".to_owned());
219 }
220
221 prereqs
222 }
223
224 fn extract_problem(steps: &[super::EnhancedStep]) -> String {
225 if let Some(first_step) = steps.first() {
226 first_step.math_context.equation.clone()
227 } else {
228 "Unknown problem".to_owned()
229 }
230 }
231
232 fn extract_approach(steps: &[super::EnhancedStep]) -> String {
233 Self::determine_method(steps)
234 }
235
236 fn extract_answer(steps: &[super::EnhancedStep]) -> String {
237 if let Some(last_step) = steps.last() {
238 last_step
239 .api_data
240 .outputs
241 .get("solution")
242 .or_else(|| last_step.api_data.outputs.get("result"))
243 .unwrap_or(&"Solution in progress".to_owned())
244 .clone()
245 } else {
246 "No solution yet".to_owned()
247 }
248 }
249
250 fn extract_insights(steps: &[super::EnhancedStep]) -> Vec<String> {
251 steps
252 .iter()
253 .filter(|step| step.message_key.message_type == "insight")
254 .map(|step| step.human_message.clone())
255 .collect()
256 }
257
258 fn suggest_next_steps(_steps: &[super::EnhancedStep]) -> Vec<String> {
259 vec![
260 "Try solving similar equations".to_owned(),
261 "Practice with different coefficients".to_owned(),
262 "Explore quadratic equations".to_owned(),
263 ]
264 }
265}
266
267impl From<super::EnhancedStep> for crate::educational::step_by_step::Step {
269 fn from(enhanced_step: super::EnhancedStep) -> Self {
270 Self::new(enhanced_step.title, enhanced_step.human_message)
271 }
272}
273
274impl From<EnhancedStepExplanation> for crate::educational::step_by_step::StepByStepExplanation {
275 fn from(enhanced_explanation: EnhancedStepExplanation) -> Self {
276 let legacy_steps: Vec<crate::educational::step_by_step::Step> = enhanced_explanation
277 .steps
278 .into_iter()
279 .map(|enhanced_step| enhanced_step.into())
280 .collect();
281
282 Self::new(legacy_steps)
283 }
284}