Files
Reklamator/app/models/feedback.py
T

257 lines
7.5 KiB
Python
Raw Normal View History

"""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