Files
recommend/modules/data_collection.py

368 lines
15 KiB
Python
Raw Permalink Normal View History

"""
数据采集模块 - 基于基座架构
负责收集用户数据问卷和餐食记录
"""
from typing import Dict, List, Optional, Any
import json
from datetime import datetime, date
from core.base import BaseModule, ModuleType, UserData, AnalysisResult, BaseConfig
class DataCollectionModule(BaseModule):
"""数据采集模块"""
def __init__(self, config: BaseConfig):
super().__init__(config, ModuleType.DATA_COLLECTION)
self.questionnaire_templates = self._load_questionnaire_templates()
def initialize(self) -> bool:
"""初始化模块"""
try:
self.logger.info("数据采集模块初始化中...")
self.is_initialized = True
self.logger.info("数据采集模块初始化完成")
return True
except Exception as e:
self.logger.error(f"数据采集模块初始化失败: {e}")
return False
def process(self, input_data: Any, user_data: UserData) -> AnalysisResult:
"""处理数据采集请求"""
try:
request_type = input_data.get('type', 'unknown')
if request_type == 'questionnaire':
result = self._process_questionnaire(input_data, user_data)
elif request_type == 'meal_record':
result = self._process_meal_record(input_data, user_data)
elif request_type == 'feedback':
result = self._process_feedback(input_data, user_data)
else:
result = self._create_error_result("未知的请求类型")
return AnalysisResult(
module_type=self.module_type,
user_id=user_data.user_id,
input_data=input_data,
result=result,
confidence=0.9 if result.get('success', False) else 0.1
)
except Exception as e:
self.logger.error(f"处理数据采集请求失败: {e}")
return self._create_error_result(str(e))
def _process_questionnaire(self, input_data: Dict, user_data: UserData) -> Dict[str, Any]:
"""处理问卷数据"""
questionnaire_type = input_data.get('questionnaire_type', 'basic')
answers = input_data.get('answers', {})
# 根据问卷类型处理答案
if questionnaire_type == 'basic':
processed_data = self._process_basic_questionnaire(answers)
elif questionnaire_type == 'taste':
processed_data = self._process_taste_questionnaire(answers)
elif questionnaire_type == 'physiological':
processed_data = self._process_physiological_questionnaire(answers)
else:
processed_data = answers
# 更新用户数据
user_data.profile.update(processed_data)
user_data.updated_at = datetime.now().isoformat()
return {
'success': True,
'processed_data': processed_data,
'message': f'{questionnaire_type}问卷处理完成'
}
def _process_meal_record(self, input_data: Dict, user_data: UserData) -> Dict[str, Any]:
"""处理餐食记录"""
meal_data = {
'date': input_data.get('date', datetime.now().strftime('%Y-%m-%d')),
'meal_type': input_data.get('meal_type', 'unknown'),
'foods': input_data.get('foods', []),
'quantities': input_data.get('quantities', []),
'calories': input_data.get('calories'),
'satisfaction_score': input_data.get('satisfaction_score'),
'notes': input_data.get('notes', ''),
'timestamp': datetime.now().isoformat()
}
# 添加到用户餐食记录
user_data.meals.append(meal_data)
user_data.updated_at = datetime.now().isoformat()
return {
'success': True,
'meal_data': meal_data,
'message': '餐食记录保存成功'
}
def _process_feedback(self, input_data: Dict, user_data: UserData) -> Dict[str, Any]:
"""处理用户反馈"""
feedback_data = {
'date': input_data.get('date', datetime.now().strftime('%Y-%m-%d')),
'recommended_foods': input_data.get('recommended_foods', []),
'user_choice': input_data.get('user_choice', ''),
'feedback_type': input_data.get('feedback_type', 'unknown'),
'satisfaction_score': input_data.get('satisfaction_score'),
'notes': input_data.get('notes', ''),
'timestamp': datetime.now().isoformat()
}
# 添加到用户反馈记录
user_data.feedback.append(feedback_data)
user_data.updated_at = datetime.now().isoformat()
return {
'success': True,
'feedback_data': feedback_data,
'message': '反馈记录保存成功'
}
def _process_basic_questionnaire(self, answers: Dict) -> Dict[str, Any]:
"""处理基础信息问卷"""
return {
'name': answers.get('name', ''),
'age': int(answers.get('age', 0)),
'gender': answers.get('gender', ''),
'height': float(answers.get('height', 0)),
'weight': float(answers.get('weight', 0)),
'activity_level': answers.get('activity_level', ''),
'health_goals': answers.get('health_goals', [])
}
def _process_taste_questionnaire(self, answers: Dict) -> Dict[str, Any]:
"""处理口味偏好问卷"""
return {
'taste_preferences': {
'sweet': int(answers.get('sweet', 3)),
'salty': int(answers.get('salty', 3)),
'spicy': int(answers.get('spicy', 3)),
'sour': int(answers.get('sour', 3)),
'bitter': int(answers.get('bitter', 3)),
'umami': int(answers.get('umami', 3))
},
'dietary_preferences': answers.get('dietary_preferences', []),
'allergies': answers.get('allergies', []),
'dislikes': answers.get('dislikes', [])
}
def _process_physiological_questionnaire(self, answers: Dict) -> Dict[str, Any]:
"""处理生理信息问卷"""
return {
'is_female': answers.get('gender') == '',
'menstrual_cycle_length': int(answers.get('menstrual_cycle_length', 28)),
'last_period_date': answers.get('last_period_date', ''),
'ovulation_symptoms': answers.get('ovulation_symptoms', []),
'zodiac_sign': answers.get('zodiac_sign', ''),
'personality_traits': answers.get('personality_traits', [])
}
def _load_questionnaire_templates(self) -> Dict[str, Dict]:
"""加载问卷模板"""
return {
'basic': {
'title': '基本信息问卷',
'questions': {
'name': {'question': '您的姓名', 'type': 'text'},
'age': {'question': '您的年龄', 'type': 'number', 'min': 1, 'max': 120},
'gender': {'question': '性别', 'type': 'select', 'options': ['', '']},
'height': {'question': '身高 (cm)', 'type': 'number', 'min': 100, 'max': 250},
'weight': {'question': '体重 (kg)', 'type': 'number', 'min': 30, 'max': 200},
'activity_level': {
'question': '日常活动水平',
'type': 'select',
'options': ['久坐', '轻度活动', '中度活动', '高度活动', '极高活动']
},
'health_goals': {
'question': '健康目标 (可多选)',
'type': 'checkbox',
'options': ['减肥', '增肌', '维持体重', '提高免疫力', '改善消化']
}
}
},
'taste': {
'title': '口味偏好问卷',
'questions': {
'sweet': {'question': '甜味偏好 (1-5分)', 'type': 'scale', 'min': 1, 'max': 5},
'salty': {'question': '咸味偏好 (1-5分)', 'type': 'scale', 'min': 1, 'max': 5},
'spicy': {'question': '辣味偏好 (1-5分)', 'type': 'scale', 'min': 1, 'max': 5},
'sour': {'question': '酸味偏好 (1-5分)', 'type': 'scale', 'min': 1, 'max': 5},
'bitter': {'question': '苦味偏好 (1-5分)', 'type': 'scale', 'min': 1, 'max': 5},
'umami': {'question': '鲜味偏好 (1-5分)', 'type': 'scale', 'min': 1, 'max': 5},
'dietary_preferences': {
'question': '饮食限制 (可多选)',
'type': 'checkbox',
'options': ['素食', '纯素食', '无麸质', '无乳制品', '无坚果', '低钠', '低碳水', '无糖']
},
'allergies': {
'question': '过敏食物 (可多选)',
'type': 'checkbox',
'options': ['花生', '坚果', '海鲜', '鸡蛋', '牛奶', '大豆', '小麦', '无过敏']
},
'dislikes': {
'question': '不喜欢的食物类型',
'type': 'checkbox',
'options': ['内脏', '海鲜', '蘑菇', '香菜', '洋葱', '大蒜', '辛辣食物', '甜食']
}
}
},
'physiological': {
'title': '生理信息问卷',
'questions': {
'menstrual_cycle_length': {
'question': '月经周期长度 (天)',
'type': 'number',
'min': 20,
'max': 40,
'optional': True
},
'last_period_date': {
'question': '上次月经日期',
'type': 'date',
'optional': True
},
'ovulation_symptoms': {
'question': '排卵期症状 (可多选)',
'type': 'checkbox',
'options': ['乳房胀痛', '情绪波动', '食欲变化', '疲劳', '无特殊症状'],
'optional': True
},
'zodiac_sign': {
'question': '星座',
'type': 'select',
'options': ['白羊座', '金牛座', '双子座', '巨蟹座', '狮子座', '处女座',
'天秤座', '天蝎座', '射手座', '摩羯座', '水瓶座', '双鱼座'],
'optional': True
},
'personality_traits': {
'question': '性格特征 (可多选)',
'type': 'checkbox',
'options': ['外向', '内向', '理性', '感性', '冒险', '保守', '创新', '传统'],
'optional': True
}
}
}
}
def get_questionnaire_template(self, questionnaire_type: str) -> Optional[Dict]:
"""获取问卷模板"""
return self.questionnaire_templates.get(questionnaire_type)
def get_all_questionnaire_types(self) -> List[str]:
"""获取所有问卷类型"""
return list(self.questionnaire_templates.keys())
def _create_error_result(self, error_message: str) -> Dict[str, Any]:
"""创建错误结果"""
return {
'success': False,
'error': error_message,
'message': f'数据采集失败: {error_message}'
}
def cleanup(self) -> bool:
"""清理资源"""
try:
self.logger.info("数据采集模块清理完成")
return True
except Exception as e:
self.logger.error(f"数据采集模块清理失败: {e}")
return False
# 便捷函数
def collect_questionnaire_data(user_id: str, questionnaire_type: str, answers: Dict) -> bool:
"""收集问卷数据"""
from core.base import get_app_core
app = get_app_core()
input_data = {
'type': 'questionnaire',
'questionnaire_type': questionnaire_type,
'answers': answers
}
result = app.process_user_request(ModuleType.DATA_COLLECTION, input_data, user_id)
return result and result.result.get('success', False)
def record_meal(user_id: str, meal_data: Dict) -> bool:
"""记录餐食"""
from core.base import get_app_core
app = get_app_core()
input_data = {
'type': 'meal_record',
**meal_data
}
result = app.process_user_request(ModuleType.DATA_COLLECTION, input_data, user_id)
return result and result.result.get('success', False)
def record_feedback(user_id: str, feedback_data: Dict) -> bool:
"""记录反馈"""
from core.base import get_app_core
app = get_app_core()
input_data = {
'type': 'feedback',
**feedback_data
}
result = app.process_user_request(ModuleType.DATA_COLLECTION, input_data, user_id)
return result and result.result.get('success', False)
if __name__ == "__main__":
# 测试数据采集模块
from core.base import BaseConfig, initialize_app, cleanup_app
print("测试数据采集模块...")
# 初始化应用
config = BaseConfig()
if initialize_app(config):
print("✅ 应用初始化成功")
# 测试问卷数据收集
test_user_id = "test_user_001"
questionnaire_answers = {
'name': '小美',
'age': 25,
'gender': '',
'height': 165,
'weight': 55,
'activity_level': '中度活动',
'health_goals': ['维持体重', '提高免疫力']
}
if collect_questionnaire_data(test_user_id, 'basic', questionnaire_answers):
print("✅ 基础问卷数据收集成功")
# 测试餐食记录
meal_data = {
'date': '2024-01-15',
'meal_type': 'breakfast',
'foods': ['燕麦粥', '香蕉', '牛奶'],
'quantities': ['1碗', '1根', '200ml'],
'calories': 350.0,
'satisfaction_score': 4,
'notes': '很满意,营养均衡'
}
if record_meal(test_user_id, meal_data):
print("✅ 餐食记录成功")
# 清理应用
cleanup_app()
print("✅ 应用清理完成")
else:
print("❌ 应用初始化失败")
print("数据采集模块测试完成!")