""" PostProcessor handles cleaning and normalizing the generated nutrition plan. """ import logging import re logger = logging.getLogger(__name__) class PostProcessor: @staticmethod def create_processor() -> "PostProcessor": return PostProcessor() def validate_and_clean(self, generated_text: str) -> str: if "DISCLAIMER" not in generated_text: generated_text += "\n\nDISCLAIMER\nThis nutrition plan is AI-generated guidance and not a medical prescription. Consult a veterinarian for medical conditions or major diet changes." generated_text = self._ensure_plain_text(generated_text) logger.info("Post-processing completed") return generated_text def extract_nutrition_values(self, final_plan: str) -> dict: values = {} lines = final_plan.split('\n') for line in lines: line = line.strip() if line.startswith('RER:'): try: values['rer'] = float(line.split('kcal/day')[0].split('RER:')[1].strip()) except: pass elif line.startswith('MER:'): try: values['mer'] = float(line.split('kcal/day')[0].split('MER:')[1].strip()) except: pass elif line.startswith('Daily Calorie Target:'): try: values['daily_calorie_target'] = float(line.split('kcal/day')[0].split('Daily Calorie Target:')[1].strip()) except: pass elif line.startswith('Protein:') and 'g/day' in line: try: values['protein_g'] = float(line.split('g/day')[0].split('Protein:')[1].strip()) except: pass elif line.startswith('Fat:') and 'g/day' in line: try: values['fat_g'] = float(line.split('g/day')[0].split('Fat:')[1].strip()) except: pass elif line.startswith('Carbohydrates:') and 'g/day' in line: try: values['carbohydrates_g'] = float(line.split('g/day')[0].split('Carbohydrates:')[1].strip()) except: pass elif line.startswith('Water Intake:'): try: values['water_ml'] = float(line.split('ml/day')[0].split('Water Intake:')[1].strip()) except: pass return values def parse_plan_sections(self, final_plan: str) -> dict: sections = {} lines = final_plan.split('\n') current_section = None section_content = [] for line in lines: line = line.strip() if line.startswith('PET NUTRITION PLAN'): current_section = 'header' section_content = [line] elif line.startswith('DAILY NUTRITION TARGETS'): if current_section: sections[current_section] = '\n'.join(section_content).strip() current_section = 'nutrition_targets' section_content = [line] elif line.startswith('RECOMMENDED MEAL PLAN'): if current_section: sections[current_section] = '\n'.join(section_content).strip() current_section = 'meal_plan' section_content = [line] elif line.startswith('FEEDING INSTRUCTIONS'): if current_section: sections[current_section] = '\n'.join(section_content).strip() current_section = 'feeding_instructions' section_content = [line] elif line.startswith('FOOD CATEGORIES'): if current_section: sections[current_section] = '\n'.join(section_content).strip() current_section = 'food_categories' section_content = [line] elif line.startswith('SAFETY NOTES'): if current_section: sections[current_section] = '\n'.join(section_content).strip() current_section = 'safety_notes' section_content = [line] elif line.startswith('DISCLAIMER'): if current_section: sections[current_section] = '\n'.join(section_content).strip() current_section = 'disclaimer' section_content = [line] else: if current_section: section_content.append(line) if current_section: sections[current_section] = '\n'.join(section_content).strip() return sections def _ensure_plain_text(self, text: str) -> str: text = re.sub(r'^#+\s+', '', text, flags=re.MULTILINE) text = re.sub(r'\[([^\]]+)\]\([^\)]+\)', r'\1', text) text = re.sub(r'\*\*([^\*]+)\*\*', r'\1', text) text = re.sub(r'\*([^\*]+)\*', r'\1', text) return text def format_sections_for_api(self, sections: dict) -> dict: formatted = {} for key, value in sections.items(): if key == 'food_categories': formatted[key] = self._parse_food_categories(value) else: formatted[key] = [line for line in value.split('\n') if line.strip()] return formatted def _parse_food_categories(self, text: str) -> dict: result = { "dangerous": [], "avoid": [], "safe": [] } lines = text.split('\n') current_category = None for line in lines: line = line.strip() if not line: continue if line.lower().startswith('dangerous:'): current_category = 'dangerous' items_text = line.split(':', 1)[1].strip() if items_text: items = [item.strip() for item in items_text.split(',') if item.strip()] result['dangerous'].extend(items) elif line.lower().startswith('avoid:'): current_category = 'avoid' items_text = line.split(':', 1)[1].strip() if items_text: items = [item.strip() for item in items_text.split(',') if item.strip()] result['avoid'].extend(items) elif line.lower().startswith('safe:'): current_category = 'safe' items_text = line.split(':', 1)[1].strip() if items_text: items = [item.strip() for item in items_text.split(',') if item.strip()] result['safe'].extend(items) else: if current_category: items = [item.strip() for item in line.split(',') if item.strip()] result[current_category].extend(items) return result