# -*- coding: utf-8 -*- """育种统计 Schema(一期 2 个最基础:ABLUP·EBV + 配合力 GCA/SCA)""" from datetime import datetime from pydantic import BaseModel, ConfigDict, Field class RunStatsIn(BaseModel): """提交统计任务的入参。""" trait_id: int = Field(..., description="性状 id(bre_trait)") trait_code: str = Field(..., description="性状编码(bre_trait.trait_code,如 avg_fruit_weight)") year: int | None = Field(None, description="评价年份过滤;为空则全部") fixed_effects: list[str] | None = Field( None, description="固定效应因子:支持 trial_study(试验/站点) / rootstock(砧木)" ) covariate: str | None = Field( None, description="协变量(BLUP 校正):crop_load(负载量) / competition(空间竞争:相邻株数)" ) data_gate: bool = Field( True, description="数据就绪门禁(G1):每 clone/家系最小 n、系谱完整率、缺失率任一不达标则拒绝运行" ) min_clone_n: int = Field( 2, ge=0, description="每 clone/家系最小样本量(观测树数低于该值的组列入未达标)" ) min_pedigree_rate: float = Field( 0.3, ge=0.0, le=1.0, description="系谱完整率下限(有父母或组合亲本的观测树占比)" ) max_missing_rate: float = Field( 0.8, ge=0.0, le=1.0, description="表型缺失率上限(缺失=有评价记录但无该性状数值)" ) gxe: bool = Field( False, description="启用 G×E 交互随机效应(clone×site / 组合×site;year 同理)" ) gxe_env: str = Field( "site", description="G×E 环境维度:site(trial_study_id) / year(evaluate_year)" ) gxr: bool = Field( False, description="启用砧木×接穗随机互作(G×R):rootstock 从固定效应移到随机效应,与 gxe 互斥" ) spatial: bool = Field( False, description=( "启用 AR1×AR1 空间协方差(残差 e~N(0,σ²e·R),R_ij=ρ^(|Δrow|+|Δcol|)):" "需观测株均有 bre_tree.row_no/col_no;与 gxe/gxr 互斥" ), ) spatial_aniso: bool = Field( False, description=( "空间协方差各向异性双参数(需 spatial=True):R_ij=ρ_row^|Δrow|·ρ_col^|Δcol|," "方法 AR1×AR1(aniso),输出 ρ_row/ρ_col;False 时用单参 ρ(v1 兼容)" ), ) block: bool = Field( False, description=( "启用区组随机效应(不完全区组/增广/α-格子):bre_tree.block_no 作第二随机效应," "精度收益进入遗传评估;需 ≥2 个区组;与 gxe/gxr/spatial 互斥" ), ) stage: str | None = Field( None, description="发育阶段拆分:juvenile(童期)/evaluation(成株);给定则只取该阶段观测建模并拆独立批次" ) design_type: str = Field( "full_diallel", description=( "交配设计(配合力分析用):full_diallel=完全双列(Griffing) / " "partial_diallel=部分双列 / line_tester=line×tester(NCII 两因素模型) / " "nciii=NCIII 测交" ), ) model_config = ConfigDict(extra="ignore") class PredictionOut(BaseModel): model_config = ConfigDict(from_attributes=True) id: int model_name: str | None = None trait_id: int | None = None method: str | None = None stage: str | None = None accuracy: float | None = None train_n: int | None = None heritability: float | None = None data_version: str | None = None input_hash: str | None = None engine_version: str | None = None is_active: bool | None = None note: str | None = None created_time: datetime | None = None class PredictionValueOut(BaseModel): model_config = ConfigDict(from_attributes=True) id: int prediction_id: int germplasm_id: int | None = None tree_id: int | None = None trait_id: int | None = None predicted_value: float | None = None reliability: float | None = None pa: float | None = None rank: int | None = None class CombiningAbilityOut(BaseModel): model_config = ConfigDict(from_attributes=True) id: int model_name: str | None = None trait_id: int | None = None method: str | None = None design_type: str | None = None gca_json: dict | None = None sca_json: dict | None = None anova_json: dict | None = None class StatisticsJobOut(BaseModel): model_config = ConfigDict(from_attributes=True) id: int job_type: str | None = None status: str params_json: dict | None = None result_ref: int | None = None error_msg: str | None = None started_time: datetime | None = None finished_time: datetime | None = None # ---------- 纯 Python 统计分析(不依赖 R) ---------- class DescribeStatsIn(BaseModel): """描述性统计入参。""" trait_codes: list[str] | None = Field(None, description="数值性状列名;为空则用全部数值列") group_by: str = Field("none", description="分组维度:none/year/combination") year: int | None = Field(None, description="评价年份过滤") model_config = ConfigDict(extra="ignore") class CorrelationIn(BaseModel): """相关分析入参。""" trait_codes: list[str] | None = Field(None, description="参与相关的数值性状;至少2个") mode: str = Field( "pheno", description="相关类型:pheno=表型相关(皮尔逊,纯Python);genetic=遗传相关(成对双性状BLUP REML)", ) g_method: str = Field( "mtblup", description="遗传相关 r_g 来源(仅 mode=genetic):mtblup=成对双性状BLUP(REML精确);calo=可靠性校正EBV相关,无生效EBV批次时回退mtblup", ) year: int | None = Field(None, description="评价年份过滤") model_config = ConfigDict(extra="ignore") class SelectionIndexIn(BaseModel): """选择指数入参。""" weights: dict[str, float] | None = Field(None, description="性状->权重,缺省等权;自动归一化") year: int | None = Field(None, description="评价年份过滤(仅表型回退路径使用)") top_n: int = Field(50, description="返回排名前 N 的单株") batch_ids: dict[str, int] | None = Field(None, description="性状->ABLUP批次id,指定则用该批次的EBV作为指数输入") use_h2: bool = Field(True, description="有效权重 = 用户权重 × 遗传力(h²) 加权(仅 zsum 模式生效)") method: str = Field( "zsum", description=( "zsum=加权Z综合(轻量,可表型回退);" "smith_hazel=真Smith-Hazel b=P⁻¹Ga(需各性状指定EBV批次且含h²);" "restricted=约束指数 b=P⁻¹G(I−M)a(Kempthorne-Nordskog,受限性状 ΔG=0,需 restricted_traits)" ), ) aggregate: str = Field( "clone", description=( "聚合粒度:clone=无性系级(同系多株可靠加权聚合为一个遗传实体再排名,桃无性繁殖默认);" "tree=单株级(旧行为)" ), ) stage: str | None = Field( None, description="发育阶段过滤:juvenile(童期)/evaluation(成株);不传且性状横跨两阶段时返回 stage_warning" ) g_method: str = Field( "calo", description="遗传相关 r_g 来源(smith_hazel / restricted 生效):calo=可靠性校正EBV相关;mtblup=成对双性状BLUP(REML精确估计,单对不收敛回退calo)" ) auto_weights: bool = Field( False, description="自动权重:以各性状 default_h2(实测h²优先,兜底0.1)为权重;强制 use_h2=False 防双重相乘", ) restricted_traits: list[str] | None = Field( None, description="受限性状列表(仅 method=restricted 生效):这些性状的遗传增益被约束为 0,其余性状自由响应(Kempthorne-Nordskog 闭式投影)" ) economic_weights: dict[str, float] | None = Field( None, description="经济权重(经济价值,绝对尺度不归一化;smith_hazel / restricted 生效):直接作为聚合基因型系数 a,未列出性状按 0 处理" ) model_config = ConfigDict(extra="ignore") class SelectionIndexOut(BaseModel): model_config = ConfigDict(from_attributes=True) id: int model_name: str | None = None method: str | None = None weights_json: dict | None = None batch_refs_json: dict | None = None heritability_json: dict | None = None top_n: int | None = None result_json: dict | None = None created_time: datetime | None = None class SelectionIndexApplyIn(BaseModel): """选择指数 -> 决选 入参。""" top_n: int = Field(50, description="写入前 N 名单株") selection_year: int | None = Field(None, description="入选年份;为空用当年") rule_id: int | None = Field(None, description="来源选择规则(决策预览选定,用于溯源 rule_id/from_stage/to_stage)") min_reliability: float = Field( 0.2, ge=0.0, le=1.0, description="EBV 可靠性硬门槛:指数引用批次的 EBV 可靠性低于该值的单株跳过(证据不足不入选)", ) model_config = ConfigDict(extra="ignore") class KinshipIn(BaseModel): """亲缘/近交分析入参。""" threshold: float = Field(0.25, description="亲缘系数预警阈值(r>A 预警)") tree_ids: list[int] | None = Field(None, description="限定单株范围;为空用全部活动单株") model_config = ConfigDict(extra="ignore") class DataQualityIn(BaseModel): """数据质量/异常值诊断入参。""" trait_id: int = Field(..., description="性状 id(bre_trait)") trait_code: str = Field(..., description="性状编码") year: int | None = Field(None, description="评价年份过滤;为空则全部") trial_study_id: int | None = Field(None, description="限定试验/站点(MET);为空则全部") dataset_id: int | None = Field( None, description="基因型数据集:提供时附加孟德尔检验段(最新批次计数+flag树+隔离状态)") model_config = ConfigDict(extra="ignore") class GeneticGainIn(BaseModel): """ΔG 遗传增益投影入参。""" trait_id: int = Field(..., description="性状 id(bre_trait)") trait_code: str = Field(..., description="性状编码") prediction_id: int | None = Field(None, description="指定 EBV 批次;为空用该性状最新批次") top_p: float | None = Field(None, gt=0, lt=1, description="入选比例(0-1,与 top_n 二选一)") top_n: int | None = Field(None, ge=1, description="入选株数(与 top_p 二选一)") generation_interval: float = Field(1.0, gt=0, description="世代间隔(年);年增益=ΔG/L") model_config = ConfigDict(extra="ignore") class InbreedingDepressionIn(BaseModel): """近交衰退分析入参。""" trait_id: int = Field(..., description="性状 id(bre_trait)") trait_code: str = Field(..., description="性状编码") year: int | None = Field(None, description="评价年份过滤;为空则全部") trial_study_id: int | None = Field(None, description="限定试验/站点(MET);为空则全部") min_n: int = Field(10, ge=2, description="回归最小样本量(同时具备 F 与表型的树)") model_config = ConfigDict(extra="ignore") class TrialDesignIn(BaseModel): """试验设计生成入参(rcbd / augmented / alpha)。""" trial_study_id: int = Field(..., description="试验研究点 id(bre_trial_study)") design_type: str = Field("rcbd", description="设计类型:rcbd=随机完全区组、augmented=增广、alpha=α-格子") seed: int | None = Field(None, description="随机种子;固定 seed 可复现同一设计") check_germplasm_ids: list[int] = Field(default_factory=list, description="增广设计对照种质 id 列表(每区组重复)") block_size: int | None = Field(None, description="α-格子区组大小 k(每重复区组块大小)") reps: int | None = Field(None, description="α-格子重复数 r(平方格子缺省 k+1)") model_config = ConfigDict(extra="ignore") class MatingRecommendIn(BaseModel): """主动选配推荐入参。""" candidate_germplasm_ids: list[int] = Field(..., description="候选亲本种质 id 集合(≥2)") prediction_id: int | None = Field(None, description="EBV 批次;为空用最新 ABLUP/GBLUP 批次") kinship_threshold: float = Field(0.25, description="亲缘惩罚阈值(r>阈值开始扣分)") w_ebv: float = Field(1.0, ge=0, description="EBV 互补权重") w_kin: float = Field(1.0, ge=0, description="近交惩罚权重") max_pairs: int = Field(20, ge=1, le=500, description="返回 top N 配对") model_config = ConfigDict(extra="ignore") class OcsIn(BaseModel): """最优贡献选择(OCS)入参。""" candidate_germplasm_ids: list[int] = Field(..., description="候选亲本种质 id 集合") n_select: int = Field(5, ge=1, description="选择数量(贡献总和 Σc=n_select)") lam: float = Field(0.1, ge=0, description="近交约束权重 λ:越大越压低子代平均亲缘(avg_kinship 单调非增),0=无近交约束(满额投最高 EBV 单亲)") prediction_id: int | None = Field(None, description="EBV 批次;为空用最新 ABLUP/GBLUP 批次") model_config = ConfigDict(extra="ignore") class MabcIn(BaseModel): """MABC 标记辅助回交进度入参。""" candidate_tree_ids: list[int] = Field(..., description="候选回交单株树 id 集合(BC 世代分离群体)") foreground_panel_ids: list[int] = Field(..., description="前景选择面板 id(目标性状 MAS 面板)") background_panel_ids: list[int] = Field(..., description="背景恢复面板 id(全基因组标记面板)") recurrent_parent_tree_id: int = Field(..., description="轮回亲本树 id(背景纯合一致对照)") foreground_min_hits: int = Field(1, ge=1, description="前景通过阈值(有利剂量命中标记数 ≥ 此值,stage 无关、童期可用)") generation: str = Field("BC1", description="候选当前回交代(F1/BC1/BC2/BC3),用于晋级建议") background_target: float = Field(90.0, gt=0, le=100, description="背景恢复目标 %(达到则建议晋级下一回交代)") model_config = ConfigDict(extra="ignore") class AnovaIn(BaseModel): """ANOVA / 广义遗传力 H² 入参。""" trait_id: int = Field(..., description="性状 id(bre_trait)") trait_code: str = Field(..., description="性状编码") year: int | None = Field(None, description="评价年份过滤;为空则全部") block: bool = Field( False, description="RCBD 设计基 ANOVA:True=从残差析出区组效应(SS_block/F_block/p_block),遗传差异为区组校正后;需观测株 bre_tree.block_no") model_config = ConfigDict(extra="ignore") class AnovaOut(BaseModel): model_config = ConfigDict(from_attributes=True) id: int model_name: str | None = None trait_id: int | None = None method: str | None = None result_json: dict | None = None created_time: datetime | None = None class CvRunIn(BaseModel): """k-fold 交叉验证入参。""" trait_id: int = Field(..., description="性状 id(bre_trait)") trait_code: str = Field(..., description="性状编码") year: int | None = Field(None, description="评价年份过滤;为空则全部") fixed_effects: list[str] | None = Field( None, description="固定效应因子:trial_study(试验/站点) / rootstock(砧木)" ) covariate: str | None = Field( None, description="协变量:crop_load(负载量) / competition(空间竞争)" ) gxe: bool = Field(False, description="启用 G×E 交互随机效应") gxe_env: str = Field("site", description="G×E 环境维度:site / year") k: int = Field(5, ge=2, le=10, description="折数") dataset_id: int | None = Field( None, description="基因型数据集 id;给定则走 GS 交叉验证(GBLUP/ssGBLUP/rrBLUP/BayesB)" ) method: str | None = Field( None, description="GS 方法:gblup / ssgblup / rrblup / bayesb(仅 dataset_id 给定时生效)" ) maf_min: float = Field(0.05, description="MAF 下限(GS 交叉验证用)") split: str = Field( "random", description="GS 折划分:random=固定种子随机分层(默认)/ family=家系阻塞折(同父半同胞同折,防亲缘泄漏)" ) seed: int = Field(20260805, description="BayesB 随机种子(折划分与 Gibbs 共用,固定可复现)") model_config = ConfigDict(extra="ignore") class CvResultOut(BaseModel): model_config = ConfigDict(from_attributes=True) id: int trait_id: int | None = None trait_code: str | None = None method: str | None = None k: int | None = None n_total: int | None = None n_individuals: int | None = None mean_pearson: float | None = None mean_rmse: float | None = None pooled_pearson: float | None = None pooled_rmse: float | None = None cv_accuracy: float | None = None h2: float | None = None data_version: str | None = None input_hash: str | None = None engine_version: str | None = None note: str | None = None created_time: datetime | None = None class CvFoldOut(BaseModel): model_config = ConfigDict(from_attributes=True) id: int cv_result_id: int fold_idx: int | None = None n_train: int | None = None n_test: int | None = None n_eval: int | None = None pearson: float | None = None rmse: float | None = None error: str | None = None class DecisionPreviewIn(BaseModel): """选择规则 × 表型/EBV/标记(MAS) 决策预览入参。""" rule_ids: list[int] | None = Field(None, description="指定规则;为空用全部启用规则") prediction_id: int | None = Field(None, description="EBV 源批次;为空用最新 ABLUP 批次") year: int | None = Field(None, description="表型均值年份过滤") min_reliability: float = Field( 0.2, ge=0.0, le=1.0, description="EBV 可靠性门槛:单株 EBV 可靠性低于该值视为证据不足,不参与晋级/淘汰判定;" "ebv 条件内可配 min_reliability 逐性状覆盖", ) stage: str | None = Field( None, description="发育阶段过滤:juvenile(童期)/evaluation(成株);不传且规则引用性状横跨两阶段时返回 stage_warning" ) model_config = ConfigDict(extra="ignore") class DecisionPreviewOut(BaseModel): rule_id: int | None = None rule_name: str | None = None action: str | None = None stage: str | None = None matched: bool | None = None reasons: list[str] | None = None tree_id: int | None = None tree_no: str | None = None combination_id: int | None = None # ---------- AMMI / Finlay-Wilkinson 稳定性(§8.18) ---------- class StabilityRunIn(BaseModel): """稳定性分析入参。""" trait_id: int = Field(..., description="性状 id(bre_trait)") trait_code: str = Field(..., description="性状编码") gxe_env: str = Field("site", description="环境维度:site(trial_study_id) / year(evaluate_year)") year: int | None = Field(None, description="评价年份过滤;为空则全部") methods: list[str] = Field( ["ammi", "finlay"], description="分析方法:ammi / finlay,可多选" ) model_config = ConfigDict(extra="ignore") class StabilityOut(BaseModel): model_config = ConfigDict(from_attributes=True) id: int trait_id: int | None = None trait_code: str | None = None env_dim: str | None = None methods: str | None = None detail_json: dict | None = None data_version: str | None = None input_hash: str | None = None engine_version: str | None = None note: str | None = None created_time: datetime | None = None # ---------- 遗传相关矩阵(MT-BLUP,§8.18) ---------- class GeneticCorrIn(BaseModel): """遗传相关矩阵入参。""" trait_ids: list[int] = Field(..., description="参与遗传相关的性状 id(≥2)") year: int | None = Field(None, description="评价年份过滤;为空则全部") full_mtblup: bool = Field( False, description="True=全多变量 EM-REML(一次估计完整 G0⊗A,替代逐对 bivariate)") model_config = ConfigDict(extra="ignore") class GeneticCorrOut(BaseModel): model_config = ConfigDict(from_attributes=True) id: int trait_ids_json: dict | None = None matrix_json: dict | None = None sigma_a_json: dict | None = None heritability_json: dict | None = None pairs_json: dict | None = None n_common: int | None = None data_version: str | None = None input_hash: str | None = None engine_version: str | None = None note: str | None = None created_time: datetime | None = None # ---------- Type-B 多环境遗传相关(§8.27) ---------- class TypeBIn(BaseModel): """Type-B 多环境遗传相关入参。""" trait_id: int = Field(..., description="性状 id(bre_trait,数值型)") trait_code: str = Field(..., description="性状编码") env_dim: str = Field("site", description="环境维度:site(研究点 trial_study_id) / year(评价年份) / stage(发育阶段:juvenile童期 vs evaluation成株,估幼年-成年遗传相关)") method: str = Field( "reml", description="方法:reml=环境互为性状逐对REML双性状BLUP(默认);calo=分环境EBV相关/√rel(无环境级EBV时回退reml)" ) year: int | None = Field(None, description="评价年份过滤;为空则全部") model_config = ConfigDict(extra="ignore") class TypeBOut(BaseModel): model_config = ConfigDict(from_attributes=True) id: int trait_id: int | None = None trait_code: str | None = None env_dim: str | None = None method: str | None = None envs_json: dict | None = None matrix_json: dict | None = None pairs_json: dict | None = None n_common: int | None = None data_version: str | None = None input_hash: str | None = None engine_version: str | None = None note: str | None = None created_time: datetime | None = None # ---------- UPGMA 聚类(§8.27,计算端点不落库) ---------- class ClusterIn(BaseModel): """UPGMA 聚类入参(实体=单株或无性系,按多性状轮廓聚类)。""" trait_ids: list[int] = Field(..., description="参与聚类的数值性状 id(≥2)") entity_type: str = Field("tree", description="聚类实体:tree=单株;clone=无性系(同系多株均值聚合)") mode: str = Field("pheno", description="值来源:pheno=表型均值;genetic=EBV(无生效批次回退表型)") distance: str = Field( "corr", description="实体距离:corr=1-|皮尔逊r|(轮廓相似);euclidean=标准化欧氏距离" ) k: int | None = Field(None, description="聚成 k 类;为空按合并距离最大跳变自动选择") model_config = ConfigDict(extra="ignore") # ---------- GBLUP / ssGBLUP 基因组选择(§8.18) ---------- class GblupRunIn(BaseModel): """GBLUP/ssGBLUP/rrBLUP/BayesB 基因组选择入参(§8.18)。""" dataset_id: int = Field(..., description="基因型数据集 id(bre_genotyping_dataset)") trait_id: int = Field(..., description="性状 id(bre_trait)") trait_code: str = Field(..., description="性状编码") year: int | None = Field(None, description="评价年份过滤;为空则全部") method: str = Field("gblup", description="gblup(仅基因型株)/ ssgblup(单步法,含非基因型亲属)/ rrblup(岭回归逐标记,输出 marker effects)/ bayesb(贝叶斯可变选择,固定 seed 可复现)") maf_min: float = Field(0.05, ge=0.0, lt=0.5, description="MAF 过滤下限") seed: int = Field(20260805, description="BayesB 随机种子(固定保证可复现;MLOps 铁律)") model_config = ConfigDict(extra="ignore") class GenotypingDatasetOut(BaseModel): id: int dataset_name: str | None = None platform: str | None = None panel: str | None = None purpose: str | None = None run_date: datetime | None = None n_samples: int | None = None # ---------- GWAS / QTL / MAS 标记辅助选择(§8.22) ---------- class GwasRunIn(BaseModel): """GWAS 关联分析入参(GLM+PC 首版)。""" dataset_id: int = Field(..., description="基因型数据集 id(bre_genotyping_dataset)") trait_id: int = Field(..., description="性状 id(bre_trait)") trait_code: str = Field(..., description="性状编码") year: int | None = Field(None, description="评价年份过滤;为空则全部") method: str = Field("gwas", description="gwas(GLM+PC)/ emmax(混合模型 EMMAX)/ ssgwas(单步 GWAS)") maf_min: float = Field(0.05, ge=0.0, lt=0.5, description="MAF 过滤下限") n_pc: int = Field(3, ge=1, le=10, description="群体结构主成分数") sig_level: float = Field(0.05, gt=0.0, le=1.0, description="显著性水平") qtl_window: int = Field(1_000_000, ge=1, description="QTL 合并窗口(bp)") model_config = ConfigDict(extra="ignore") class GwasQtlXEIn(BaseModel): """QTL×E 入参(按环境分层 GWAS + 稳定性判定,计算端点不建表)。""" dataset_id: int = Field(..., description="基因型数据集 id(bre_genotyping_dataset)") trait_id: int = Field(..., description="性状 id(bre_trait)") trait_code: str = Field(..., description="性状编码") year: int | None = Field(None, description="评价年份过滤;为空则全部") method: str = Field("gwas", description="gwas(GLM+PC)/ emmax(混合模型 EMMAX)") maf_min: float = Field(0.05, ge=0.0, lt=0.5, description="MAF 过滤下限") n_pc: int = Field(3, ge=1, le=10, description="群体结构主成分数") sig_level: float = Field(0.05, gt=0.0, le=1.0, description="显著性水平") qtl_window: int = Field(1_000_000, ge=1, description="QTL 合并窗口(bp)") env_dim: str = Field("site", description="环境维度:site(trial_study_id) / year(evaluate_year)") model_config = ConfigDict(extra="ignore") class GwasResultOut(BaseModel): model_config = ConfigDict(from_attributes=True) id: int dataset_id: int | None = None trait_id: int | None = None trait_code: str | None = None method: str | None = None n_individuals: int | None = None n_markers: int | None = None m_after_maf: int | None = None maf_min: float | None = None n_pc: int | None = None sig_level: float | None = None threshold_bonf: float | None = None n_sig_bonf: int | None = None n_sig_fdr: int | None = None n_qtl: int | None = None data_version: str | None = None input_hash: str | None = None engine_version: str | None = None remark: str | None = None created_time: datetime | None = None class GwasSnpOut(BaseModel): model_config = ConfigDict(from_attributes=True) id: int gwas_result_id: int marker_id: int | None = None marker_name: str | None = None chromosome: str | None = None position: int | None = None maf: float | None = None effect: float | None = None se: float | None = None t_value: float | None = None p_value: float | None = None neg_log10p: float | None = None q_value: float | None = None sig_bonf: bool | None = None sig_fdr: bool | None = None class QtlIn(BaseModel): """已知 QTL 录入(source=known)入参。""" trait_id: int | None = Field(None, description="性状 id(bre_trait)") chromosome: str | None = Field(None, description="染色体") start_bp: int | None = Field(None, description="区间起点(bp)") end_bp: int | None = Field(None, description="区间终点(bp)") peak_marker_id: int | None = Field(None, description="峰标记 id(bre_marker)") peak_p: float | None = Field(None, description="峰标记 p 值") n_markers: int | None = Field(None, description="区间显著标记数") effect: float | None = Field(None, description="峰效应") remark: str | None = Field(None, description="备注") model_config = ConfigDict(extra="ignore") class QtlOut(BaseModel): model_config = ConfigDict(from_attributes=True) id: int trait_id: int | None = None chromosome: str | None = None start_bp: int | None = None end_bp: int | None = None peak_marker_id: int | None = None peak_marker_name: str | None = None peak_p: float | None = None n_markers: int | None = None effect: float | None = None source: str | None = None gwas_result_id: int | None = None remark: str | None = None created_time: datetime | None = None class MasPanelIn(BaseModel): """MAS 标记辅助选择面板入参。""" panel_name: str = Field(..., description="面板名称") trait_id: int | None = Field(None, description="目标性状 id(bre_trait)") remark: str | None = Field(None, description="备注") model_config = ConfigDict(extra="ignore") class MasPanelOut(BaseModel): model_config = ConfigDict(from_attributes=True) id: int panel_name: str | None = None trait_id: int | None = None n_markers: int | None = None remark: str | None = None created_time: datetime | None = None class MasPanelMarkerIn(BaseModel): """面板标记(有利剂量 + 方向 + 基因作用模式)。""" marker_id: int | None = Field(None, description="标记 id(bre_marker)") favorable_dose: int | None = Field(None, description="有利剂量(0/1/2)") effect: float | None = Field(None, description="效应(参考)") direction: str | None = Field("high", description="high=剂量≥favorable_dose命中 / low=剂量≤命中") mode: str | None = Field("additive", description="基因作用模式:additive/dominance/recessive/allele/haplotype") favorable_allele: str | None = Field(None, description="有利等位(allele/haplotype 模式用,SSR 等位索引)") haplotype_group: str | None = Field(None, description="单倍型组(haplotype 模式:同组标记全命中才计 1)") model_config = ConfigDict(extra="ignore") class MasPanelSetMarkersIn(BaseModel): """面板标记全量替换入参。""" markers: list[MasPanelMarkerIn] = Field(default_factory=list, description="面板标记列表") model_config = ConfigDict(extra="ignore")