"""Feedback model""" import os import uuid from datetime import datetime import yaml from flask import current_app class Feedback: """Feedback submission model Attributes: feedback_id: Unique feedback identifier (UUID) product_id: Associated product ID submitted_at: Submission timestamp (ISO 8601) status: Feedback status ('new', 'analyzing', 'analyzed', 'analysis_failed', 'archived') content_preview: First 200 chars of feedback text has_attachments: Whether feedback has file attachments attachment_count: Number of attached files original_language: Detected language of feedback (set during analysis) category: Feedback category (set during analysis) """ VALID_STATUSES = ['new', 'analyzing', 'analyzed', 'analysis_failed', 'archived'] def __init__(self, feedback_id, product_id, submitted_at=None, status='new', content_preview='', has_attachments=False, attachment_count=0, original_language=None, category=None): self.feedback_id = feedback_id self.product_id = product_id self.submitted_at = submitted_at or datetime.utcnow().isoformat() self.status = status self.content_preview = content_preview self.has_attachments = has_attachments self.attachment_count = attachment_count self.original_language = original_language self.category = category def to_dict(self): """Convert feedback to dictionary Returns: dict: Feedback metadata """ data = { 'feedback_id': self.feedback_id, 'product_id': self.product_id, 'submitted_at': self.submitted_at, 'status': self.status, 'content_preview': self.content_preview, 'has_attachments': self.has_attachments, 'attachment_count': self.attachment_count } if self.original_language: data['original_language'] = self.original_language if self.category: data['category'] = self.category return data @classmethod def from_dict(cls, data): """Create feedback from dictionary Args: data: Dictionary with feedback data Returns: Feedback: Feedback instance """ return cls( feedback_id=data['feedback_id'], product_id=data['product_id'], submitted_at=data.get('submitted_at'), status=data.get('status', 'new'), content_preview=data.get('content_preview', ''), has_attachments=data.get('has_attachments', False), attachment_count=data.get('attachment_count', 0), original_language=data.get('original_language'), category=data.get('category') ) @staticmethod def generate_id(): """Generate unique feedback ID Returns: str: UUID-based feedback ID """ return str(uuid.uuid4()) @staticmethod def _get_feedback_dir(product_id, feedback_id): """Get feedback directory path Args: product_id: Product ID feedback_id: Feedback ID Returns: str: Path to feedback directory """ return os.path.join( current_app.config['DATA_DIR'], 'products', product_id, 'feedback', feedback_id ) @staticmethod def _get_metadata_file(product_id, feedback_id): """Get metadata file path Args: product_id: Product ID feedback_id: Feedback ID Returns: str: Path to metadata.yaml """ feedback_dir = Feedback._get_feedback_dir(product_id, feedback_id) return os.path.join(feedback_dir, 'metadata.yaml') @staticmethod def _get_content_file(product_id, feedback_id): """Get content file path Args: product_id: Product ID feedback_id: Feedback ID Returns: str: Path to content.txt """ feedback_dir = Feedback._get_feedback_dir(product_id, feedback_id) return os.path.join(feedback_dir, 'content.txt') @staticmethod def _get_attachments_dir(product_id, feedback_id): """Get attachments directory path Args: product_id: Product ID feedback_id: Feedback ID Returns: str: Path to attachments directory """ feedback_dir = Feedback._get_feedback_dir(product_id, feedback_id) return os.path.join(feedback_dir, 'attachments') @classmethod def get_by_id(cls, product_id, feedback_id): """Load feedback by ID Args: product_id: Product ID feedback_id: Feedback ID Returns: Feedback or None: Feedback instance if found, None otherwise """ metadata_file = cls._get_metadata_file(product_id, feedback_id) if not os.path.exists(metadata_file): return None with open(metadata_file, 'r') as f: data = yaml.safe_load(f) return cls.from_dict(data) @classmethod def get_all_for_product(cls, product_id): """Get all feedback for a product Args: product_id: Product ID Returns: list: List of Feedback instances, sorted by submitted_at (newest first) """ feedback_list = [] feedback_base_dir = os.path.join( current_app.config['DATA_DIR'], 'products', product_id, 'feedback' ) if not os.path.exists(feedback_base_dir): return feedback_list for feedback_id in os.listdir(feedback_base_dir): feedback_dir = os.path.join(feedback_base_dir, feedback_id) if not os.path.isdir(feedback_dir): continue feedback = cls.get_by_id(product_id, feedback_id) if feedback: feedback_list.append(feedback) # Sort by submitted_at (newest first) feedback_list.sort(key=lambda f: f.submitted_at, reverse=True) return feedback_list def save_metadata(self): """Save feedback metadata to filesystem""" feedback_dir = self._get_feedback_dir(self.product_id, self.feedback_id) os.makedirs(feedback_dir, exist_ok=True) metadata_file = self._get_metadata_file(self.product_id, self.feedback_id) with open(metadata_file, 'w') as f: yaml.dump(self.to_dict(), f, default_flow_style=False) def get_content(self): """Load feedback content text Returns: str or None: Feedback content if exists, None otherwise """ content_file = self._get_content_file(self.product_id, self.feedback_id) if not os.path.exists(content_file): return None with open(content_file, 'r') as f: return f.read() def get_attachments(self): """Get list of attachment filenames Returns: list: List of attachment filenames """ attachments_dir = self._get_attachments_dir(self.product_id, self.feedback_id) if not os.path.exists(attachments_dir): return [] return [f for f in os.listdir(attachments_dir) if os.path.isfile(os.path.join(attachments_dir, f))] def validate_status(self): """Validate feedback status Returns: bool: True if status is valid, False otherwise """ return self.status in self.VALID_STATUSES