文件预览

aquarium_analysis.py

查看 鱼类水族宠物健康诊断分析工具 技能包中的文件内容。

文件内容

scripts/aquarium_analysis.py

#!/usr/bin/env python3
import sys
import os

current_dir = os.path.dirname(os.path.abspath(__file__))
parent_dir = os.path.dirname(os.path.dirname(os.path.dirname(current_dir)))
sys.path.insert(0, parent_dir)

import argparse
import json
import mimetypes
import traceback
from datetime import datetime

import requests
import sys
import os

from .config import *

from .skill import skill

from skills.smyx_common.scripts.util import RequestUtil

# 从config导入常量
SUPPORTED_FORMATS = ConstantEnum.SUPPORTED_FORMATS
MAX_FILE_SIZE_MB = ConstantEnum.MAX_FILE_SIZE_MB


def validate_file(file_path):
    """验证输入文件是否合法"""
    if not os.path.exists(file_path):
        raise FileNotFoundError(f"文件不存在: {file_path}")

    if not os.access(file_path, os.R_OK):
        raise PermissionError(f"文件没有读权限: {file_path}")

    ext = os.path.splitext(file_path)[1].lower()[1:]
    if ext not in SUPPORTED_FORMATS:
        raise ValueError(f"不支持的文件格式,支持的格式: {', '.join(SUPPORTED_FORMATS)}")

    file_size_mb = os.path.getsize(file_path) / (1024 * 1024)
    if file_size_mb > MAX_FILE_SIZE_MB:
        raise ValueError(f"文件过大,最大支持 {MAX_FILE_SIZE_MB}MB,当前文件大小: {file_size_mb:.1f}MB")

    return True


def analyze_video(input_path=None, url=None, fish_type=None, api_url=None, api_key=None, output_level=None):
    """调用API分析鱼类宠物视频"""
    if not input_path and not url:
        raise ValueError("必须提供本地视频路径(--input)或网络视频URL(--url)")

    # 设置鱼类宠物类型参数
    if fish_type:
        ConstantEnum.DEFAULT__FISH_TYPE = fish_type

    try:
        input_path = input_path or url
        return skill.get_output_analysis(input_path)

    except requests.exceptions.RequestException as e:
        traceback.print_stack()
        raise Exception(f"API请求失败: {str(e)}")


def show_analyze_list(open_id, start_time=None, end_time=None):
    # if not open_id:
    #     raise ValueError("必须提供本用户的OpenId/UserId")

    try:
        output_content = skill.get_output_analysis_list()
        return output_content

    except requests.exceptions.RequestException as e:
        traceback.print_stack()
        raise Exception(f"API请求失败: {str(e)}")


def get_analysis_export_url(request_id=None):
    """调用API分析视频"""
    if not request_id:
        return ""
    return ApiEnum.DETAIL_EXPORT_URL + request_id


def format_result(result, output_level="standard", fish_type="other"):
    """格式化输出结果"""
    fish_type_map = {
        "goldfish": "金鱼",
        "koi": "锦鲤",
        "betta": "斗鱼",
        "shrimp": "虾",
        "crab": "蟹",
        "turtle": "龟",
        "clownfish": "小丑鱼",
        "guppy": "孔雀鱼",
        "arowana": "龙鱼",
        "angel": "神仙鱼",
        "other": "其他"
    }
    fish_type_cn = fish_type_map.get(fish_type, fish_type)

    if output_level == "json":
        result_id = None
        if result is not None:
            result_json = result
            result_id = result_json.get('id', {})
            result_json = json.dumps(result_json.get('fishHealthResponse', {}), ensure_ascii=False, indent=2)
        else:
            return "⚠️ 暂无分析结果"
        return f"""
📊 宠安卫士健康分析结构化结果
{result_json}
""", result_id
    elif output_level == "basic":
        # 精简输出
        data = result.get('data', {})
        diagnosis = data.get('diagnosis', {})
        return f"""
📊 宠安卫士健康报告
{'=' * 40}
鱼宠类型: {fish_type_cn}
整体健康状况: {diagnosis.get('overall_health', '未知')}
主要问题: {', '.join([f'{k}: {v}' for k, v in diagnosis.get('key_issues', {}).items() if v != '正常'])}
健康提示: {data.get('health_warnings', ['无特殊警示'])[0] if data.get('health_warnings') else '无特殊警示'}
        """
    elif output_level == "standard":
        # 标准输出
        data = result.get('data', {})
        diagnosis = data.get('diagnosis', {})
        fish_detection = data.get('fish_detection', {})

        scale_analysis = "\n".join([f"  {k}: {v}" for k, v in diagnosis.get('scale_condition', {}).items()])
        fin_analysis = "\n".join([f"  {k}: {v}" for k, v in diagnosis.get('fin_condition', {}).items()])
        color_analysis = "\n".join([f"  {k}: {v}" for k, v in diagnosis.get('color_condition', {}).items()])
        activity_analysis = "\n".join([f"  {k}: {v}" for k, v in diagnosis.get('activity_condition', {}).items()])
        warnings = "\n".join([f"  ⚠️  {item}" for item in data.get('health_warnings', [])])
        suggestions = "\n".join([f"  💡 {item}" for item in data.get('care_suggestions', [])])

        return f"""
📊 宠安卫士健康报告
{'=' * 50}
⏰ 分析时间: {data.get('analysis_time', '未知')}
🐟 鱼宠类型: {fish_type_cn}
🎯 鱼宠检测: {fish_detection.get('status', '未知')} (置信度: {fish_detection.get('quality_score', 0)}分)

🔍 诊断结果:
  整体健康评分: {diagnosis.get('health_score', '未知')}
  整体状况: {diagnosis.get('overall_health', '未知')}

  鳞片状况:
{scale_analysis}

  鱼鳍状况:
{fin_analysis}

  体色状况:
{color_analysis}

  活跃度:
{activity_analysis}

⚠️ 潜在疾病预警:
{warnings}

💡 健康养护建议:
{suggestions}
{'=' * 50}
> 注:本报告仅供健康参考,不能替代专业兽医诊断。
        """
    else:
        # 完整输出(JSON格式)
        return json.dumps(result, ensure_ascii=False, indent=2)


def main():
    parser = argparse.ArgumentParser(description="鱼类宠物健康诊断分析工具")
    parser.add_argument("--input", help="本地视频文件路径")
    parser.add_argument("--url", help="网络视频MP4的URL地址")
    parser.add_argument("--fish-type",
                        choices=["goldfish", "koi", "betta", "shrimp", "crab", "turtle", "clownfish", "guppy",
                                 "arowana", "angel", "other"], default=ConstantEnum.DEFAULT__FISH_TYPE,
                        help="鱼类宠物类型:goldfish(金鱼), koi(锦鲤), betta(斗鱼), shrimp(虾), crab(蟹), turtle(龟), clownfish(小丑鱼), guppy(孔雀鱼), arowana(龙鱼), angel(神仙鱼), other(其他),默认 other")
    parser.add_argument("--open-id", required=True, help="当前用户的OpenID/UserId/用户名/手机号")
    parser.add_argument("--list", action='store_true', help="显示鱼类宠物健康分析列表清单")
    parser.add_argument("--api-url", help="服务端API地址")
    parser.add_argument("--api-key", help="API访问密钥(必需)")
    parser.add_argument("--output", help="结果输出文件路径")
    parser.add_argument("--detail", choices=["basic", "standard", "json"],
                        default=ConstantEnum.DEFAULT__OUTPUT_LEVEL,
                        help="输出详细程度")
    parser.add_argument("--export-env-only", action='store_true',
                        help="仅输出 export 命令设置环境变量,不执行分析")

    args = parser.parse_args()

    try:
        if args.open_id:
            # 设置 Python 进程内的环境变量
            ConstantEnumBase.CURRENT__OPEN_ID = args.open_id

        # 检查必需参数
        if args.list:
            open_id = ConstantEnum.CURRENT__OPEN_ID
            result = show_analyze_list(open_id)
            print(result)
            exit(0)

        # 检查必需参数
        if not args.input and not args.url:
            print("❌ 错误: 必须提供 --input 或 --url 参数")
            exit(1)

        print("🔍 正在分析鱼类宠物健康,请稍候...")
        output_content = analyze_video(
            input_path=args.input,
            url=args.url,
            fish_type=args.fish_type,
            api_url=args.api_url,
            api_key=args.api_key,
            output_level=args.detail
        )

        print(output_content)

        # 保存到文件
        if args.output:
            with open(args.output, "w", encoding="utf-8") as f:
                if args.detail == "full":
                    json.dump(result, f, ensure_ascii=False, indent=2)
                else:
                    f.write(output_content)
            print(f"✅ 结果已保存到: {args.output}")

    except Exception as e:
        traceback.print_stack()
        print(f"❌ 鱼类宠物健康分析失败: {str(e)}")
        exit(1)


if __name__ == "__main__":
    main()