import pandas as pd
import numpy as np
import time
import public as pb
import openpyxl
import matplotlib.pyplot as plt
from scipy.stats import spearmanr
# import tkinter as tk
# from tkinter import ttk
from tkinter import filedialog
from tkinter import messagebox
from openpyxl import load_workbook
from openpyxl.drawing.image import Image as OImage
import os
import re
import ttkbootstrap as ttk
from ttkbootstrap.constants import *
from PIL import Image, ImageTk
from ttkbootstrap.dialogs import Messagebox
import plotly.graph_objects as go
import plotly.io as pio
from sklearn.linear_model import LinearRegression
from openpyxl.worksheet.hyperlink import Hyperlink
from scipy.ndimage import gaussian_filter1d
from scipy.stats import norm
from plotly.subplots import make_subplots
import docx
from docx import Document
from docx.shared import Inches
from docx.oxml import OxmlElement, ns
from docx.shared import Pt, RGBColor
from docx.oxml.ns import nsdecls, nsmap
from docx.oxml import parse_xml
from docx.enum.dml import MSO_THEME_COLOR_INDEX
from docx import Document
from docx.opc.constants import RELATIONSHIP_TYPE as RT
from docx.enum.table import WD_TABLE_ALIGNMENT, WD_CELL_VERTICAL_ALIGNMENT
from docx.oxml.ns import qn
from docx.enum.text import WD_ALIGN_PARAGRAPH
# 显示所有数据
pd.set_option('display.width', 10000) # 设置字符显示宽度
pd.set_option('display.max_rows', None) # 设置显示最大行
pd.set_option('display.max_columns', None) # 设置显示最大列,None为显示所有列

# 设置字体
# 设置字体 微软雅黑 新罗马 加粗
plt.rcParams['font.family'] = ['Times New Roman', 'Microsoft YaHei']
# 设置字体加粗
font = {'weight': 'bold'}
plt.rc('font', **font)  # 应用字体设置


indexClassificationList = {
    '物理指标': ['pH', '土壤质地', '土壤容重1(g/cm³)', '土壤容重2(g/cm³)', '土壤容重3(g/cm³)',	'土壤容重4(g/cm³)',	'土壤容重平均值(g/cm³)',
                 '2~0.2mm颗粒含量', '0.2~0.02mm颗粒含量',	'0.02~0.002mm颗粒含量',	'0.002mm以下颗粒含量', '水稳>5mm(%)',	'水稳3mm~5mm(%)',
                 '水稳2mm~3mm(%)',	'水稳1mm~2mm(%)',	'水稳0.5mm~1mm(%)',	'水稳0.25mm~0.5mm(%)',	'水稳性大团聚体总和(%)', '洗失量(吸管法需填)', '风干试样含水量(分析基)'],
    '常规养分指标': ['有机质', '全氮', '全磷', '全钾', '有效磷', '速效钾', '有效硫', '有效硼', '有效铁', '有效锰', '有效铜', '有效锌', '有效钼', '有效硅', '缓效钾'],
    '一般化学性指标': ['阳离子交换量', '交换性盐基总量', '交换性钙', '交换性镁', '交换性钠', '交换性钾', '全盐量', '电导率',
                       '水溶性Na⁺含量', '水溶性K⁺含量',	'水溶性Ca²⁺含量',	'水溶性Mg²⁺含量',	'水溶性Cl⁻含量', '水溶性CO₃²⁻含量','水溶性HCO₃⁻含量',
                       '水溶性SO₄²⁻含量', '离子总量', '碳酸钙',
                       '游离铁', '全硫', '全锰', '全锌', '全铜', '全钼', '全硼', '全硒', '全铝', '全硅', '全铁', '全钙', '全镁'],
    '重金属指标': ['总汞', '总砷', '总铅', '总镉', '总铬', '总镍']
}

# 可交互绘图函数
def getInteractiveImg(x,y,label,x1,y1,label1,x2,y2,label2,url,name,xLabel,YLabel,numArr):
    # coef, p_value = spearmanr(x, y)
    # 绘制数据散点图
    fig = go.Figure(data=go.Scatter(
        x=x,
        y=y,
        text=numArr.to_numpy(),
        mode='markers', name=label,
        marker=dict(
            size=4,  # 点的大小
            color='blue',  # 点的颜色
        ))
    )
    # 设置图表布局
    fig.update_layout(
                      title={
                          'text': f"{name}",
                          'xanchor': 'center',  # 控制水平对齐,可选'left', 'center', 'right'
                          'yanchor': 'bottom',  # 控制垂直对齐,可选'top', 'middle', 'bottom'
                          'x': 0.5,  # 控制标题的水平位置,0.5代表中心,可以是小数(相对位置)或整数(像素位置)
                          'y': 0.9  # 控制标题的垂直位置,0.9代表底部,可以是小数或整数
                      },
                      xaxis_title=xLabel,
                      yaxis_title=YLabel)
    if label == 'pH':
        ph_y_f = [7.5 for _ in x]
        ph_y_s = [8.0 for _ in x]
        fig.add_trace(go.Scatter(x=x, y=ph_y_f, mode='lines', name='pH = 7.5', line=dict(
            width=1.5,
            color='#339933',
            dash='dash'  # 设置虚线样式
        )))
        fig.add_trace(go.Scatter(x=x, y=ph_y_s, mode='lines', name='pH = 8.0', line=dict(
            width=2.5,
            color='#339933',
            dash='dash'  # 设置虚线样式
        )))
    if len(x1) > 0 and len(y1) > 0:
        fig.add_trace(go.Scatter(x=x1, y=y1, mode='markers', name=label1, text=numArr.to_numpy(),
                                 marker=dict(
                                    size=10,        # 点的大小
                                    color='red',    # 点的颜色
                                    symbol='hourglass'
                                )))
    if len(x2) > 0 and len(y2) > 0:
        fig.add_trace(go.Scatter(x=x2, y=y2, mode='markers', name=label2, text=numArr.to_numpy(),
                                marker=dict(
                                    size=10,        # 点的大小
                                    color='green',    # 点的颜色
                                    symbol='triangle-up'  # 点的形状,这里设置为正方形
                                )))
    # model = LinearRegression()
    # model.fit(x.to_numpy().reshape(-1, 1), y)  # 用x的平方作为特征值
    # y_pred = model.predict(x.to_numpy().reshape(-1, 1))
    # fig.add_trace(go.Scatter(x=x, y=y_pred, mode='lines', name='拟合直线'))

    html_file_path = f"{url}/{name}.html"
    pio.write_html(fig, file=html_file_path, auto_open=False)
    # 同时保存一份图片
    try:
        print(pio.kaleido.scope.plotlyjs)
        print(f"图片将保存至 " + f"{url}/{name}.png")
        fig.write_image(f"{url}/{name}.png",scale=3, width=800, height=600)
        print(f"图片已成功保存至 "+f"{url}/{name}.png")
    except Exception as e:
        print(f"保存图片时出现错误: {str(e)}")
    # 在文档中中插入html
    # workbook = load_workbook(filename=fileUrl)
    # # 选择一个工作表
    # ws = workbook[sheetName]
    # # 将 HTML 内容作为富文本写入单元格
    # ws[loc] = '=HYPERLINK("file:///{0}","点击查看统计图")'.format(html_file_path)
    # workbook.save(fileUrl)



# 循环确定数据属于哪一个类型
def getDataType(data, list):

    for item in list:
        if str(str(data['原样品编号'])[6:10]) in list[item]:
           data['土地类型归类'] = item

    return data

# 1. 统计样品数量 分类统计:耕地:0101 0102 0103 园地:0201 0202 0203 0204  林地:0301 0302 0303 0304 0305 0306 0307 草地:0401 0402 0403 0404
def getSimpleNum(data):
    """
    :param simpleData: 样品数据
    :return:
    """
    typeList = {
        '耕地园地': ['0101', '0102', '0103', '0201', '0202', '0203', '0204'],
        '林地草地': ['0301', '0302', '0303', '0304', '0305', '0306', '0307', '0401', '0402', '0403', '0404'],
        '商服用地': ['0501', '0502', '0503', '0504', '0505', '0506', '0507'],
        '工矿仓储用地': ['0601', '0602', '0603', '0604'],
        '住宅用地': ['0701', '0702'],
        '公共管理与公共服务用地': ['0801', '0802', '0803', '0804', '0805', '0806', '0807', '0808', '0809', '0810'],
        '特殊用地': ['0901', '0902', '0903', '0904', '0905', '0906'],
        '交通运输用地': ['1001', '1002', '1003', '1004', '1005', '1006', '1007', '1008', '1009'],
        '水域及水利设施用地': ['1101', '1102', '1103', '1104', '1105', '1106', '1107', '1108', '1109', '1110'],
        '其他土地': ['1201', '1202', '1203', '1204', '1205', '1206', '1207']
    }
    # 根据耕地园地、林地草地分类数据,统计不同类型数据数量,生成表格
    newData = pd.DataFrame({})
    for index, row in data.iterrows():
        newRow = getDataType(row, typeList)
        newData =newData._append(newRow)

    # 按照土地类型归类 分类计数
    grouped_df = newData.groupby('土地类型归类')
    counts = grouped_df.size()
    resData = pd.DataFrame({})

    for group_name, group_data in grouped_df:
        # 附表:数量总体统计数据
        res = {
            '类型': group_name,
            '样品编号': group_data['样品编号'].to_list(),
            '数量': counts[group_name],
            '合计': counts.sum()
        }
        newRes = pd.DataFrame(res)
        resData = resData._append(newRes)

    return {
        'sData': counts, # 统计数量
        'allData': resData # 附表总数
    }

# 2.小数修改:进行中
def is_one_decimal_place(num): # 判断是否保留一位小数
    print('检测小数1', num, str(num).count('.') == 1 and len(str(num).split('.')[1]) == 1)
    return (str(num).count('.') == 1 and len(str(num).split('.')[1]) == 1)

def has_two_decimals(num): # 判断是否保留了两位小数
    print('检测小数2', num, str(num).count('.') == 1 and len(str(num).split('.')[1]) == 2)
    return  (str(num).count(".") == 1 and len(str(num).split(".")[1]) == 2)

def three_decimal(num):
    # 不超过三位有效数字
    num = str(num)
    num = num.replace('.', '')
    numLen = 0
    stripped_s = num.lstrip('0')
    if stripped_s and stripped_s[0].isdigit():
        index = num.index(stripped_s[0])  # 加上去除的0的个数
        numLen = len(num[index:])
      # 字符串全为0或不包含数字时
    return numLen

def is_less_than_three_decimals(number): # 不超过3位小数
    # 转换为字符串
    number_str = str(number)
    # 分离整数部分和小数部分
    integer_part, decimal_part = number_str.split('.') if '.' in number_str else (number_str, '')
    # 判断小数部分是否不超过三位
    return len(decimal_part) <= 3

# 保留3位小数
def is_three_decimal(number):
    number_str = str(number)
    # 分离整数部分和小数部分
    integer_part, decimal_part = number_str.split('.') if '.' in number_str else (number_str, '')
    # 判断小数部分是否不超过三位
    return len(decimal_part) == 3

def highlight_condition(s):
    if s['数据审核结果'] != '' and not pd.isna(s['数据审核结果']):
        return ['background-color: #99CC99']*len(s)
    else:
        return ['']*len(s)


def filter_number(arr):
    """
    :param arr:
    :return:
    """
    return pd.to_numeric(arr, errors='coerce')
def getNum(data, url):
    # 读取数据 处理每一项小数保留问题
    oneData = ['2~0.2mm颗粒含量','0.2~0.02mm颗粒含量','0.02~0.002mm颗粒含量','0.002mm以下颗粒含量', '洗失量(吸管法需填)',
               '水稳>5mm','水稳3mm~5mm','水稳2mm~3mm','水稳1mm~2mm','水稳0.5mm~1mm','水稳0.25mm~0.5mm','水稳性大团聚体总和'
               ]
    twoData = ['土壤容重1(g/cm³)','土壤容重2(g/cm³)','土壤容重3','土壤容重4','土壤容重平均值','pH']
    threeData = ['电导率','水溶性钠离子','水溶性钾离子','水溶性钙离子','水溶性镁离子','水溶性碳酸根','水溶性碳酸氢根','水溶性硫酸根','水溶性氯根','离子总量'] # 3位有效数字
    twoDataToThree = ['有机质','全氮','全磷','全钾','全硫','全硼','全硒','全铁'] #保留2位小数,最多不超过3位有效数字
    threeDataToThree = ['有效钼', '总汞']
    changeDataList = ['阳离子交换量', '交换性盐基总量', '交换性钙', '交换性镁', '交换性钠', '交换性钾'] # <10 2位小数 >=10 3位有效数字
    cationDataList = ['全锰', '全铜', '全锌', '速效钾', '缓效钾', '有效硅', '总铅', '总镉', '总铬', '总镍'] # <1000 2位小数 不超过3位有效数字 >=1000 保留整数
    needData = data.iloc[:, 7:]
    if '土壤质地' in needData.columns:
        del needData['土壤质地']
    # needData = needData.apply(pd.to_numeric, errors='coerce')
    checkDataRes = []
    for index, row in needData.iterrows():
        str = ''
        for item in row.index:
            if item in oneData and (not pd.isna(row[item])) and not is_one_decimal_place(row[item]):
                str += f"{item}应保留一位小数。"
            if (item in twoData) and (not pd.isna(row[item])) and not has_two_decimals(row[item]):
                str += f"{item}应保留两位小数。"
            if item in threeData and (not pd.isna(row[item])) and three_decimal(row[item]) != 3:
                str += f"{item}应保留三位有效数字。"
            if item in twoDataToThree and (not pd.isna(row[item])) and (not has_two_decimals(row[item]) or three_decimal(row[item]) > 3):
                str += f"{item}应保留两位小数且不超过三位有效数字。"
            if item in threeDataToThree and (not pd.isna(row[item])) and (not is_less_than_three_decimals(row[item]) or three_decimal(row[item]) != 3 ):
                str += f"{item}应保留三位有效数字且不超过三位小数。"
            if item in changeDataList and (not pd.isna(row[item])) and (filter_number(row[item]) < 10) and not has_two_decimals(row[item]):
                str += f"{item}应保留两位小数。"
            if item in changeDataList and (not pd.isna(row[item])) and (filter_number(row[item]) >= 10) and three_decimal(row[item]) != 3:
                str += f"{item}应保留三位有效数字。"
            if item == '碳酸钙' and (not pd.isna(row[item])) and not isinstance(row[item], int):
                str += f"{item}应为整数。"
            if item == '全盐量' and (not pd.isna(row[item])) and not is_three_decimal(row[item]):
                str += f"{item}应保留3位小数。"
            if item in cationDataList and (not pd.isna(row[item])) and (filter_number(row[item]) < 1000) and (not has_two_decimals(row[item]) or three_decimal(row[item]) > 3):
                str += f"{item}保留2位小数且不超过三位有效数字。"
            if item in cationDataList and (not pd.isna(row[item])) and (filter_number(row[item]) >= 1000) and not isinstance(row[item], int):
                str += f"{item}应为整数。"
        checkDataRes.append(str)
    data['数据审核结果'] = checkDataRes
    # 对审核结果有问题的数据进行标绿
    resData = data.style.apply(highlight_condition, axis=1)
    # 数据写入表格
    with pd.ExcelWriter( f'{url}/数值修约审核.xlsx', engine='openpyxl') as writer:
        resData.to_excel(writer, index=False, sheet_name='数值修约')

# getNum('')
# 3.计算所有指标的频度信息 已完成
def getFrequencyInformation(data, url):
    if '数据审核结果' in data.columns:
        del data['数据审核结果']
    if '母岩' in data.columns:
        del data['母岩']
    #统计样品数量 计算最大值 最小值 中位数 平均值 标准差
    needData = data.iloc[:, 7:]
    if '土壤质地' in needData.columns:
        del needData['土壤质地']

    needData = needData.apply(pd.to_numeric, errors="coerce")
    resData = pd.DataFrame({})
    for item in needData.columns:
        if item == '总砷':
            print(needData[item])
        min_value = needData[item].min() # 最大值
        max_value = needData[item].max() # 最小值
        median_value = needData[item].median()  # 中位数
        mean_value = needData[item].mean()  # 平均数
        std_value = needData[item].std()  # 标准差
        resData[item] = [min_value, max_value, median_value, mean_value, std_value]

    index_value = ['最小值', '最大值', '中位数', '平均数', '标准差']
    # 汇总数据
    resData.index = index_value
    data_res = round(resData, 2)
    data_res = data_res.rename_axis('频度分析')
    # data_res = data_res.transpose()
    # data_res = data_res.reset_index()
    # data_res.columns = ['指标名称', '最小值', '最大值', '中位数', '平均数', '标准差']
    print('频度分析---', data_res)
    return data_res
    # 数据写入表格
    # with pd.ExcelWriter(f'{url}/频度信息.xlsx', engine='openpyxl') as writer:
    #     data_res.to_excel(writer, sheet_name='频度信息')

# getFrequencyInformation('')
# 4. 绘制每个指标的累积频率图
def getMap(data, title, unit, url):
    data = data.dropna()
    if len(data) != 0: # 指标无数据 不进行绘图
        min = data.min()
        max = data.max()
        # 计算直方图
        bins = np.linspace(min - 1, max + 1, 30, endpoint=True)
        hist, bin_edges = np.histogram(data, bins=bins)
        cumulative_hist = np.cumsum(hist)
        # 计算拟合曲线
        p1 = np.polyfit(bin_edges[1:], hist, 2)
        p2 = np.poly1d(p1)
        # 计算累计频率
        cumulative_freq = cumulative_hist / cumulative_hist.max()
        # fig, ax1 = plt.subplots()
        fig = make_subplots(specs=[[{"secondary_y": True}]])
        if title == 'pH':
            fig.add_trace(go.Histogram(x=data, name="频数", nbinsx = 17, xbins = dict(start=0, end=8.5, size=0.5)), secondary_y=True)
        else:
            fig.add_trace(go.Histogram(x=data, name="频数"), secondary_y=True)
        # 绘制直方图 color="#ffc798" #a4464b #b1a9ab
        # fig = go.Figure(data=[go.Histogram(x=data, name="频数")])
        # 设置标题和其他格式
        fig.update_layout(
            title={
                'text': f"{title}统计图",
                'xanchor': 'center',  # 控制水平对齐,可选'left', 'center', 'right'
                'yanchor': 'bottom',  # 控制垂直对齐,可选'top', 'middle', 'bottom'
                'x': 0.5,  # 控制标题的水平位置,0.5代表中心,可以是小数(相对位置)或整数(像素位置)
                'y': 0.9  # 控制标题的垂直位置,0.9代表底部,可以是小数或整数
            },
            xaxis_title=f"{title}{unit}",
            yaxis_title='频次',
            bargap=0.2,  # 相邻位置坐标的钢筋之间的间隙
            bargroupgap=0.1
        )
        # ax1.hist(data, bins=bins, rwidth=0.8, zorder=1, label="频数")
        # ax2 = ax1.twinx()
        # ax2.plot(bin_edges[1:], cumulative_freq, zorder=10, color="#a4464b", label="累积频率 ")
        fig.add_trace(go.Scatter(x=bin_edges[1:], y=cumulative_freq *100, mode='lines', name='累积频率',line=dict(width=2), yaxis='y2',
                                 marker=dict(
                                     color='red',  # 点的颜色
                                     symbol='hourglass'
                                 )), secondary_y=False)
        # 绘制正态分布曲线
        # 估计正态分布参数
        mu = data.mean()
        sigma = data.std()
        # 创建正态分布对象
        dist = norm(mu, sigma)
        # 计算要绘制的x值
        x = np.linspace(bin_edges.min(), bin_edges.max(), 100)
        p = norm.pdf(x, mu, sigma)
        # ax3 = ax1.twinx()
        # # ax3.plot(bin_edges[1:], y_smoothed, label='拟合曲线', color="#333")
        # #label = 'N({:.2f}, {:.2f}^2)'.format(mu, sigma)
        # ax3.plot(x, dist.pdf(x), color="#333", label='拟合曲线')

        fig.add_trace(go.Scatter(x=x, y=p*100, mode='lines', yaxis='y2', name='拟合曲线', line=dict(width=2)), secondary_y=False)
        fig.update_layout(
            title={
                'text': '指标频度统计图',
                'xanchor': 'center',  # 控制水平对齐,可选'left', 'center', 'right'
                'yanchor': 'bottom',  # 控制垂直对齐,可选'top', 'middle', 'bottom'
                'x': 0.5,  # 控制标题的水平位置,0.5代表中心,可以是小数(相对位置)或整数(像素位置)
                'y': 0.9  # 控制标题的垂直位置,0.9代表底部,可以是小数或整数
            },
            yaxis_title='累积频率(%)',  # 设置Y1轴标签
            yaxis2_title='频数'
                          )
        maxData = 110
        if p.max()*100 >100:
            maxData = p.max()*100
        fig.update_yaxes(range=[0, maxData], row=1, col=1, secondary_y=False)
        html_file_path = f"{url}/{title}频度统计图.html"
        html_file_path = html_file_path.replace('(g/cm³)','')
        html_file_path = html_file_path.replace('(%)', '')
        html_file_path = html_file_path.replace('>', '')
        pio.write_html(fig, file=html_file_path, auto_open=False)
        title = title.replace('(g/cm³)', '')
        title = title.replace('(%)', '')
        title = title.replace('>', '')
        pio.write_image(fig, f"{url}/{title}频度统计图.png")


def getFrequencyImage(data, url):
    unitList = {
        'g/cm3': ['土壤容重1(g/cm³)','土壤容重2(g/cm³)',	'土壤容重3(g/cm³)',	'土壤容重4(g/cm³)','土壤容重平均值(g/cm³)'],
        '%': ['土壤机械组成','全铝', '全硅', '全铁', '全钙', '全镁', '水稳>5mm(%)','水稳3mm~5mm(%)',	'水稳2mm~3mm(%)',	'水稳1mm~2mm(%)',	'水稳0.5mm~1mm(%)',	'水稳0.25mm~0.5mm(%)',	'水稳性大团聚体总和(%)'],
        'g/kg': ['有机质', '全氮', '全磷', '全钾', '水溶性盐总量', '离子总量', '碳酸钙', '游离铁', '全硫'],
        'mg/kg': ['有效磷', '速效钾', '有效硫', '有效硼', '有效铁', '有效锰', '有效铜', '有效锌', '有效钼', '有效硅', '缓效钾', '全锰',
                '全锌', '全铜', '全钼', '全硼', '全硒', '总汞', '总砷', '总铅', '总镉', '总铬', '总镍'],
        'cmol/kg': ['阳离子交换量', '交换性盐基总量', '交换性钙', '交换性镁', '交换性钠', '交换性钾']
    }
    # 绘制图形
    needData = data.iloc[:, 7:] #这里有可能需要修改
    if '土壤质地' in needData.columns:
        del needData['土壤质地']
    needData = needData.apply(pd.to_numeric, errors="coerce")
    for item in needData.columns:
        # newData = needData[item].dropna()
        if len(needData) > 0:
            label = ''
            for i in unitList:
                if item in unitList[i]:
                    label = i
            getMap(needData[item], item, label, url)

# 公共函数 判断指标完整性
# def checkAllNames(data, list):

    # for item in list:
    #     if data[item] != '未检测' and data[item] != '/': # 未检测的值可以忽略 空值
    #         if pd.isna(data[item]): # 表示值缺失


# 5. 检测指标完整性
def getDataComplete(data):
    # 根据类型:耕地园地表层土壤样品,耕地园地剖面土壤样品,林地草地表层土壤样品,林地草地剖面土壤样品,水稳定性大团聚体样品
    # 耕地园地样品列表  有效硅(水田才有)
    cultivatedLandandfieldList = ['风干试样含水量(分析基)', '洗失量(吸管法需填)', '土壤质地', '土壤容重1(g/cm³)',	'土壤容重2(g/cm³)',	'土壤容重3(g/cm³)',	'土壤容重4(g/cm³)',	'土壤容重平均值(g/cm³)'
        , '2~0.2mm颗粒含量',	'0.2~0.02mm颗粒含量',	'0.02~0.002mm颗粒含量', 	'0.002mm以下颗粒含量'
        , '水稳>5mm(%)',	'水稳3mm~5mm(%)','水稳2mm~3mm(%)',	'水稳1mm~2mm(%)','水稳0.5mm~1mm(%)','水稳0.25mm~0.5mm(%)',	'水稳性大团聚体总和(%)'
        , 'pH', '阳离子交换量', '交换性盐基总量', '交换性钙', '交换性镁', '交换性钠', '交换性钾',
        '全盐量', '电导率', '水溶性Na⁺含量', '水溶性K⁺含量', '水溶性Ca²⁺含量', '水溶性Mg²⁺含量', '水溶性Cl⁻含量', '水溶性CO₃²⁻含量',
        '水溶性HCO₃⁻含量', '水溶性SO₄²⁻含量', '离子总量', '有机质', '全氮', '全磷', '全钾', '有效磷', '速效钾', '缓效钾', '有效硫', '有效铁',
        '有效锰', '有效铜', '有效锌', '有效硼', '有效钼', '总汞', '总砷', '总铅', '总镉', '总铬', '总镍']
    # 林地草地样品列表
    woodlandandGrassList = ['风干试样含水量(分析基)', '洗失量(吸管法需填)', '土壤质地', '土壤容重1(g/cm³)',	'土壤容重2(g/cm³)',	'土壤容重3(g/cm³)',	'土壤容重4(g/cm³)',	'土壤容重平均值(g/cm³)', '2~0.2mm颗粒含量','0.2~0.02mm颗粒含量',	'0.02~0.002mm颗粒含量', '0.002mm以下颗粒含量', 'pH', '阳离子交换量', '交换性盐基总量', '交换性钙', '交换性镁', '交换性钠', '交换性钾', '有机质',
                    '全氮', '全磷', '全钾', '有效磷', '速效钾',]


    # 其他样品列表 剖面
    # '耕地': ['0101', '0102', '0103'],
    # '园地': ['0201', '0202', '0203', '0204'],
    # '林地': ['0301', '0302', '0303', '0304', '0305', '0306', '0307'],
    # '草地': ['0401', '0402', '0403', '0404'],
    # 根据土地利用类型 判断指标是否完整 不完整的数据提取出来 这里规则无法确定 先放着吧

    # 统计所有数据中 各指标的数量
    resData = getSimpleNum(data)
    yxg = 0
    for index, row in data.iterrows():
        if str(str(row['原样品编号'])[6:10]) == '0101':
            yxg += 1
    data = data.replace('未检测', np.nan)
    if '序号' in data.columns:
        del data['序号']
    if '原样品编号' in data.columns:
        del data['原样品编号']
    if '样品编号' in data.columns:
        del data['样品编号']
    if '地理位置' in data.columns:
        del data['地理位置']
    if '母质' in data.columns:
        del data['母质']
    if '土壤类型' in data.columns:
        del data['土壤类型']
    if '土地利用类型' in data.columns:
        del data['土地利用类型']
    # 根据指标排序
    counts = data.count()
    # 根据土地类型统计样本数
    countData = {
        '耕地园地': resData['sData']['耕地园地'] if '耕地园地' in resData['sData'].index else 0,
        '林地草地': resData['sData']['林地草地'] if '林地草地' in resData['sData'].index else 0,
        '其他': 0,
        '有效硅': yxg
    }
    needNumList = []
    for item in data.columns:
        # 判断指标是否属于耕地园地、林地草地数组
        if item == '有效硅':
            needNumList.append(countData['有效硅'])
        elif (item in cultivatedLandandfieldList) and (item in woodlandandGrassList):
            needNumList.append(countData['耕地园地'] + countData['林地草地'])
        elif (item in cultivatedLandandfieldList) and (item not in woodlandandGrassList):
            needNumList.append(countData['耕地园地'])
        elif (item not in cultivatedLandandfieldList) and (item in woodlandandGrassList):
            needNumList.append(countData['林地草地'])
        elif (item not in cultivatedLandandfieldList) and (item not in woodlandandGrassList):
            needNumList.append(0)
        else:
            needNumList.append(0)
    counts = counts.to_frame()
    counts['应测数量'] = needNumList
    return counts



# 6.指标名称与实际检测样品数量统计表,统计样品数量和指标检测数量
def getCheckNum():
    # 统计每个指标的有值的数量,水溶性盐分总量大于1g/kg的 统计八大离子的检测量
    resData = ''
    return resData


# 过滤特殊字符
def filter_special_characters(s):
    s = str(s)
    return re.sub(r'[^a-zA-Z\u4e00-\u9fa5\d]', '', s)

# 7.检测方法
def checkMethod(data, url):
    # 各指标的检测标准方法
    checkData = {
        '土壤容重' : '《土壤检测 第4部分:土壤容重的测定》(NY/T 1121.4—2006) 环刀法',
        '机械组成' : '《土壤分析技术规范》(第二版), 5.1 吸管法',
        '土壤水稳性大团聚体': '《土壤检测 第19部分:土壤水稳定大团聚体组成的测定》(NY/T 1121.19—2008)筛分法',
        'pH检测方法': '《土壤检测 第2部分:土壤pH的测定》(NY/T 1121.2—2006) 电位法',
        '水溶性盐类(水溶性盐总量、电导率、水溶性钠离子、钾离子、钙离子、镁离子、碳酸根、碳酸氢根、硫酸根、氯根)': '《森林土壤水溶性盐分分析》(LY/T 1251—1999)(浸提液中钙、镁、钾、钠离子的测定采用等离子体发射光谱法,硫酸根和碳酸根的测定增加离子色谱法) 质量法',
        '全氮': '《土壤检测 第24部分:土壤全氮的测定 自动定氮仪法》(NY/T 1121.24—2012) 自动定氮仪法',
        '全磷': '《森林土壤磷的测定》(LY/T 1232—2015)(详见本规范培训教材) 酸消解—电感耦合等离子体发射光谱法',
        '全钾': '《森林土壤钾的测定》(LY/T 1234—2015) 酸消解一电感耦合等离子体发射光谱法',
        '全铁': '《固体废物22种金属元素的测定》(HJ 781—2016) 酸消解—电感耦合等离子体发射光谱法',
        '全锰': '《固体废物22种金属元素的测定》(HJ 781—2016) 酸消解—电感耦合等离子体发射光谱法',
        '全钼': '《固体废物 金属元素的测定 电感耦合等离子体质谱法》(HJ 766—2015) 酸消解—电感耦合等离子体质谱法',
        '全铝': '《固体废物 22种金属元素的测定 电感耦合等离子体发射光谱法》(HJ 781—2016) 酸消解-电感耦合等离子体发射光谱法',
        '全硅': '《土壤和沉积物 11种元素的测定 碱熔—电感耦合等离子体发射光谱法》(HJ 974—2018) 碱熔一电感耦合等离子体发射光谱法	',
        '全钙': '《固体废物 22种金属元素的测定 电感耦合等离子体发射光谱法》(HJ 781—2016) 酸消解—电感耦合等离子体发射光谱法',
        '全镁': '《固体废物 22种金属元素的测定 电感耦合等离子体发射光谱法》(HJ 781—2016) 酸消解—电感耦合等离子体发射光谱法',
        '速效钾': '《土壤 速效钾和缓效钾含量的测定》(NY/T 889—2004) 乙酸铵浸提—火焰光度法',
        '缓效钾': '《土壤 速效钾和缓效钾含量的测定》(NY/T 889—2004) 热硝酸浸提—火焰光度法',
        '有效硅': '《土壤检测第15部分:土壤有效硅的测定》(NY/T 1121.15-2006) 柠檬酸浸提-硅钼蓝比色法',
        '有效硼': '土壤样品制备与检测技术规范培训教材 沸水提取-电感耦合等离子体发射光谱法',
        '有效钼': '《土壤检测 第9部分:土壤有效钼的测定》(NY/T 1121.9-2023) 草酸-草酸铵浸提-电感耦合等离子体质谱法',
        '游离铁': '《土壤分析技术规范》(第二版),19.1游离铁(Fed)的测定(DCB法) 连二亚硫酸钠-柠檬酸钠-重 碳酸提-邻菲罗啉比色法',
        '总砷': '《土壤质量 总汞、总砷、总铅的测定 原子荧光法第2部分:土壤中总砷的测定》(GB/T22105.2-2008) 原子荧光法',
        '总铅': '《固体废物 金属元素的测定电感耦合等离子体质谱法》(HJ766-2015) 酸消解-电感耦合等离子体质谱法',
        '总镉': '《固体废物 金属元素的测定电感耦合等离子体质谱法》(HJ766-2015) 酸消解-电感耦合等离子体质谱法',
        '总铬': '《固体废物 金属元素的测定电感耦合等离子体质谱法》(HJ766-2015) 酸消解-电感耦合等离子体质谱法',
        '总镍': '《固体废物 金属元素的测定电感耦合等离子体质谱法》(HJ766-2015) 酸消解-电感耦合等离子体质谱法'
    }
    checkDBData = {
        '有机质': ['《土壤检测 第6部分:土壤有机质的测定》(NY/T 1121.6—2006) 重铬酸钾氧化—容量法',
                   '土壤中总碳和有机质的测定元素分析仪法(农业行业标准报批稿) 元素分析仪法'],  # 两种方法都可以
        '全硫': ['《土壤检测 第2部分:土壤全硫的测定》(NY/T 1104—2006) 硝酸镁氧化-硫酸钡比浊法',
                 '燃烧红外光谱法(本规范培训教材)'],
        '全硼': ['《土壤分析技术规范》(第二版),18.1土壤全硼的测定 碱熔-姜黄素-比色法',
                 '《土壤分析技术规范》(第二版),18.1土壤全硼的测定 碱熔-等离子体发射光谱法'],
        '全铜': ['《固体废物 金属元素的测定电感耦合等离子体质谱法》(HJ766-2015) 酸消解-电感耦合等离子体质谱法',
                 '《固体废物 22种金属元素的测定电感耦合等离子体发射光谱法》(HJ781-2016) 酸消解-电感耦合等离子体发射光谱法'],
        '全锌': ['《固体废物 金属元素的测定电感耦合等离子体质谱法》(HJ766-2015) 酸消解一电感耦合等离子体质谱法',
                 '《固体废物 22种金属元素的测定 电感耦合等离子体发射光谱法》(HJ 781—2016) 酸消解—电感耦合等离子体发射光谱法'],
        '可交换酸度': '《土壤分析技术规范》(第二版), 11.2 土壤交换性酸的测定 氯化钾交换—中和滴定法',  # ph<6
        '碳酸钙': '《土壤分析技术规范》(第二版), 15.1 土壤碳酸盐的测定 气量法',  # ph>7
        '总汞': ['《土壤质量 总汞、总砷、总铅的测定原子荧光法 第1部分:土壤中总汞的测定》(GB/T22105.1-2008) 原子荧光法',
                 '《土壤和沉积物总汞的测定 催化热解/冷原子吸收分光光度法》(HJ923-2017) 催化热解-冷原子吸收分光光度法'],
        '有效铁': [
            '《土壤有效态锌、锰、铁、铜含量的测定 二乙三胺五乙酸(DTPA)浸提法》(NY/T8902004) DTPA 浸提-原子吸收分光光度法',
            '《土壤有效态锌、锰、铁、铜含量的测定 二乙三胺五乙酸(DTPA)浸提法》(NY/T8902004) DTPA 浸提-电感耦合等离子体发射光谱法'],
        '有效锰': [
            '《土壤有效态锌、锰、铁、铜含量的测定 二乙三胺五乙酸(DTPA)浸提法》(NY/T8902004) DTPA 浸提-原子吸收分光光度法',
            '《土壤有效态锌、锰、铁、铜含量的测定 二乙三胺五乙酸(DTPA)浸提法》(NY/T8902004) DTPA浸提-电感耦合等离子体发射光谱法'],
        '有效铜': [
            '《土壤有效态锌、锰、铁、铜含量的测定 二乙三胺五乙酸(DTPA)浸提法》(NY/T8902004) DTPA 浸提-原子吸收分光光度法',
            '《土壤有效态锌、锰、铁、铜含量的测定 二乙三胺五乙酸(DTPA)浸提法》(NY/T8902004) DTPA 浸提-电感耦合等离子体发射光谱法'],
        '有效锌': [
            '《土壤有效态锌、锰、铁、铜含量的测定 二乙三胺五乙酸(DTPA)浸提法》(NY/T8902004) DTPA 浸提-原子吸收分光光度法',
            '《土壤有效态锌、锰、铁、铜含量的测定 二乙三胺五乙酸(DTPA)浸提法》(NY/T8902004) DTPA 浸提-电感耦合等离子体发射光谱法'],
    }
    checkPHData = {
        '阳离子交换量': ['《土壤分析技术规范》(第二版), 12.2 乙酸铵交换法',
                         '《土壤分析技术规范》(第二版), 12.1 EDTA—乙酸铵盐交换法'],  # ph<=7.5 ph>7.5
        '交换性盐基总量、交换性钾、交换性钠、交换性钙、交换性镁': [
            '《土壤分析技术规范》(第二版), 13.1 酸性和中性土壤交换性盐基组分的测定(乙酸铵交换法)(交换性钙、镁、钾、钠离子的测定增加等离子体发射光谱法) 乙酸铵交换法等',
            '《石灰性土壤交换性盐基及盐基总量的测定》(NY/T 1615-2008)(交换液中钾、钠、钙、镁离子的测定增加等离子体发射光谱法) 氯化铵-乙醇交换法等'],
        # ph<=7.5 ph>7.5
        '有效磷': ['《土壤检测 第7部分:土壤有效磷的测定》(NY/T 1121.7—2014) 氟化铵-盐酸溶液浸提一钼锑抗比色法',
                   '《土壤检测 第7部分:土壤有效磷的测定》(NY/T 1121.7—2014) 碳酸氢钠溶液—钼锑抗比色法'],  # ph<6.5 ph>=6.5
        '有效硫': ['《土壤检测第14部分:土壤有效的测定》NY/T 1121.14-2023) 磷酸盐-乙酸溶液浸提-电感耦合等离子体发射光谱法',
                   '《土壤检测第14部分:土壤有效的测定》(NY/T 1121.14-2023) 氯化钙浸提一电感耦合等离子体发射光谱法'],
        # ph<7.5 ph>=7.5

    }
    checkDataKey =  [key for key in checkData]
    checkDBDataKey = ['有机质', '全硫', '全硼', '全铜', '全锌', '可交换酸度', '碳酸钙', '总汞', '有效铁', '有效锰', '有效铜', '有效锌']
        # [key for key in checkDBData]
    checkPHDataKey = ['阳离子交换量', '交换性盐基总量、交换性钾、交换性钠、交换性钙、交换性镁', '有效磷', '有效硫']
        # [key for key in checkPHData]
    # print('list',checkDBDataKey)
    # print('list', checkPHDataKey)

    checkDataRes = []
    for index, row in data.iterrows():
        str = ''
        for item in row.index:
            if row[item] == '未检测':
                str = ''
            elif row[item] == '未填写' or pd.isna(row[item]):
                str += f"{item}未填写。"
            elif not pd.isna(row[item]) and (item in checkDataKey) and (not pd.isna(checkData[item])): # 指标不为空 且 在通用指标列表中
                if filter_special_characters(row[item]) != filter_special_characters(checkData[item]):
                    str += f"{item}检测方法填报有误。"
            elif (not pd.isna(row[item]) and (item in checkDBDataKey)):
                if filter_special_characters(row[item]) != filter_special_characters(checkDBData[item][0]) and filter_special_characters(row[item]) != filter_special_characters(checkDBData[item][1]):
                    str += f"{item}检测方法填报有误。"
                # 指标在两种方法指标列表中
            elif (not pd.isna(row[item]) and (item in checkPHDataKey)):
                # 指标在区分ph值列表中
                if item == '阳离子交换量' and row['pH'] <= 7.5:
                    if filter_special_characters(row[item]) != filter_special_characters(checkPHData[item][0]):
                        str += f"{item}检测方法填写有误。"
                if item == '阳离子交换量' and row['pH'] > 7.5:
                    if filter_special_characters(row[item]) != filter_special_characters(checkPHData[item][1]):
                        str += f"{item}检测方法填写有误。"
                if item == '交换性盐基总量、交换性钾、交换性钠、交换性钙、交换性镁' and row['pH'] <= 7.5:
                    if filter_special_characters(row[item]) != filter_special_characters(checkPHData[item][0]):
                        str += f"{item}检测方法填写有误。"
                if item == '交换性盐基总量、交换性钾、交换性钠、交换性钙、交换性镁' and row['pH'] > 7.5:
                    if filter_special_characters(row[item]) != filter_special_characters(checkPHData[item][0]):
                        str += f"{item}检测方法填写有误。"
                if item == '有效磷' and row['pH'] < 6.5:
                    if filter_special_characters(row[item]) != filter_special_characters(checkPHData[item][0]):
                        str += f"{item}检测方法填写有误。"
                if item == '有效磷' and row['pH'] >= 6.5:
                    if filter_special_characters(row[item]) != filter_special_characters(checkPHData[item][0]):
                        str += f"{item}检测方法填写有误。"
                if item == '有效硫' and row['pH'] < 7.5:
                    if filter_special_characters(row[item]) != filter_special_characters(checkPHData[item][0]):
                        str += f"{item}检测方法填写有误。"
                if item == '有效硫' and row['pH'] >= 7.5:
                    if filter_special_characters(row[item]) != filter_special_characters(checkPHData[item][0]):
                        str += f"{item}检测方法填写有误。"
        checkDataRes.append(str)
    data['数据审核结果'] = checkDataRes
    resData = data.style.apply(highlight_condition, axis=1)
    # 数据写入表格
    with pd.ExcelWriter(f'{url}/检测方法审核结果.xlsx', engine='openpyxl') as writer:
        resData.to_excel(writer, index=False, sheet_name='检测方法审核')
    return data[data['检测方法审核结果'] !='']


# 8.数据填报审核
def dataReportResult(data, url):
    # 未检出项填报,空值填报,错误值处理
    # 检测值有*号、/、未检测、空值、数值 为合理情况,
    resData = []
    for index, row in data.iterrows():
        str = ''
        for item in row.index:
            if row[item] == '-' or row[item] == 0:
                str = f"{item}数据填报错误。"
        resData.append(str)
    data['数据审核结果'] = resData
    finData = data.style.apply(highlight_condition, axis=1)
    # 数据写入表格
    with pd.ExcelWriter(f'{url}/数据填报项审核结果.xlsx', engine='openpyxl') as writer:
        finData.to_excel(writer, index=False, sheet_name='数据填报审核')


# 9.土壤质地类型判断 这个之前已有,将土壤质地类型不一致的样本数据提取出来
# 使用resData数据,判断土壤类型和土壤类型判断是否一致,不一致提取出不一致数据写入表格


# 10.土壤检测数据超阈值样品统计表,将之前统计的超阈值的数据提取出来,显示原因
# def getOverLineData(data, url):  # 所有阈值判断
    # 提取数据项,异常原因,增加一列外业保持空值
    # 提取每个表格审核结果不为空的数据,最后将所有数据合并
    # resData =
    # 数据写入表格
    # with pd.ExcelWriter(f'{url}/超阈值样品统计表.xlsx', engine='openpyxl') as writer:
    #     resData.to_excel(writer, index=False, sheet_name='超阈值数据')

# 11.ph值统计 频度计算、历年数据统计、ph异常数据提取
def getPHData(data, url):
    resData = pd.DataFrame({})
    # 计算频度
    min_value = data['pH'].min()  # 最大值
    max_value = data['pH'].max()  # 最小值
    median_value = data['pH'].median()  # 中位数
    mean_value = round(data['pH'].mean(),2) # 平均数
    std_value = round(data['pH'].std(), 2)  # 标准差
    resData['PH'] = [min_value, max_value, median_value, mean_value, std_value]
    index_value = ['最小值', '最大值', '中位数', '平均数', '标准差']
    resData.index = index_value
    # 绘制分布图
    x = np.arange(0, len(data['pH']), 1) # 横坐标数据
    data = data.sort_values(by='pH', ascending=True)
    y = data['pH']
    getInteractiveImg(x, y, 'pH', [], [], '', [], [], '', url,
                      'pH值分布图', '样品序号', 'pH', data['原样品编号'])

    # 提取异常数据
    abnormalData = pd.DataFrame({})
    for index, row in data.iterrows():
        if not pd.isna(row['pH']) and (row['pH'] < mean_value-3*std_value or row['pH'] > mean_value+3*std_value):
            newRow = row[['原样品编号', '样品编号', '土地利用类型', 'pH']]
            soilType = ''
            if isinstance(row['土壤类型'], str):
                if len(row['土壤类型'].split('_')) > 1:
                    soilType = row['土壤类型'].split('_')[1]
            newRow['土壤类型'] = soilType
            newRow['外业情况'] = ''
            abnormalData = abnormalData._append(newRow)
    return {
        '异常数据': abnormalData,
        '频度分析': resData
    }
    # resData写进文档表格
    # 异常数据写入表格
    # 生成也可以写入文档表格
    # with pd.ExcelWriter( './img/ph异常数据.xlsx', engine='openpyxl') as writer:
    #     abnormalData.to_excel(writer, index=False, sheet_name='ph异常数据')
    #     resData.to_excel(writer, sheet_name='频度分析')


# 12.有机质和全氮 计算比值、绘制相关性图、提取异常数据形成表格
def getNAndC(data, url):
    # 去掉nan的值
    data = data.dropna(subset=['有机质', '全氮'])
    # 绘制散点图 拟合直线 计算方差
    x = data['有机质']
    y = data['全氮']
    plt.scatter(x, y)
    fig = go.Figure(data=go.Scatter(
        x=x,
        y=y,
        text=data['原样品编号'].to_numpy(),
        mode='markers', name='有机质与全氮',
        marker=dict(
            size=10,  # 点的大小
            color='blue',  # 点的颜色
        ))
    )

    # 使用sklearn的LinearRegression进行最小二乘法拟合
    model = LinearRegression()
    model.fit(x.to_numpy().reshape(-1, 1), y)
    # 计算拟合直线的斜率和截距
    slope = model.coef_[0]
    intercept = model.intercept_
    r, _ = np.corrcoef(y, slope * x + intercept)
    # 绘制拟合直线
    fig.add_trace(go.Scatter(x=x, y=slope * x + intercept,mode='lines', name='拟合直线'))
    # plt.plot(x, slope * x + intercept, color='red', label='拟合直线',  linewidth=2)
    # 设置图表布局
    fig.update_layout(
                      title={
                          'text': f"有机质与全氮相关性散点图,y={round(slope,2)}x + {round(intercept,2)},R²={round(r[1], 2) ** 2},R={round(r[1],1)}",
                          'xanchor': 'center',  # 控制水平对齐,可选'left', 'center', 'right'
                          'yanchor': 'bottom',  # 控制垂直对齐,可选'top', 'middle', 'bottom'
                          'x': 0.5,  # 控制标题的水平位置,0.5代表中心,可以是小数(相对位置)或整数(像素位置)
                          'y': 0.9  # 控制标题的垂直位置,0.9代表底部,可以是小数或整数
                      },
                      xaxis_title='有机质(g/kg)',
                      yaxis_title='全氮(g/kg)')
    html_file_path = f"{url}/有机质与全氮相关性散点图.html"
    pio.write_html(fig, file=html_file_path, auto_open=False)
    # 同时保存一份图片
    pio.write_image(fig, f"{url}/有机质与全氮相关性散点图.png")
    # plt.savefig('./img/审核报告图形/' + '有机质与全氮相关性散点图.png', dpi=500, bbox_inches='tight')
    # plt.show()
    abnormalData = pd.DataFrame({})
    for index, row in data.iterrows():
        if not pd.isna(row['有机质']) and not pd.isna(row['全氮']):
            if row['有机质']/row['全氮'] <13 or row['有机质']/row['全氮'] > 20:
                newRow = row[['原样品编号', '样品编号', '土地利用类型', '有机质', '全氮']]
                soilType = ''
                resStr = ''
                if row['有机质']/row['全氮'] < 13:
                    resStr = '偏低'
                if row['有机质']/row['全氮'] > 20:
                    resStr = '偏高'
                if isinstance(row['土壤类型'], str):
                    if len(row['土壤类型'].split('_')) > 1:
                        soilType = row['土壤类型'].split('_')[1]
                newRow['土壤类型'] = soilType
                newRow['碳氮比'] = round(row['有机质']/row['全氮'], 2)
                newRow['审核结果'] = resStr
                newRow['外业情况'] = ''
                abnormalData = abnormalData._append(newRow)
    return abnormalData
    # with pd.ExcelWriter('./img/碳氮比异常数据.xlsx', engine='openpyxl') as writer:
    #     abnormalData.to_excel(writer, index=False, sheet_name='碳氮比异常数据')

def getImg(x,y,label):
    plt.scatter(x, y)
    plt.xlabel('样品数量')
    plt.ylabel(label)
    # plt.savefig('./img/' + label + '数据分布图.png', dpi=500, bbox_inches='tight')
    plt.show()


# 13.全磷和有效磷,绘图,统计异常值,绘图绘制在同一个图中
def getPData(data, url):
    # 提取异常数
    abnormalData = pd.DataFrame({})
    # abnormalData.columns = ['原样品编号', '样品编号', '土地利用类型', '全磷', '有效磷', '土壤类型', '有效磷比', '外业情况']
    for index, row in data.iterrows():
        if not pd.isna(row['有效磷']) and not pd.isna(row['全磷']):
            if row['有效磷'] /(1000 * row['全磷']) >= 0.15:
                newRow = row[['原样品编号', '样品编号', '土地利用类型', '全磷', '有效磷']]
                soilType = ''
                if isinstance(row['土壤类型'], str):
                    if len(row['土壤类型'].split('_')) > 1:
                        soilType = row['土壤类型'].split('_')[1]
                newRow['土壤类型'] = soilType
                newRow['有效磷比'] = round(row['有效磷'] / (row['全磷']*10), 2)
                newRow['外业情况'] = ''
                abnormalData = abnormalData._append(newRow)

    with pd.ExcelWriter(f'{url}/有效磷占全磷比异常数据.xlsx', engine='openpyxl') as writer:
        abnormalData.to_excel(writer, index=False, sheet_name='有效磷占全磷比比异常数据')
    if not data['全磷'].empty and not data['有效磷'].empty:
        x1 = np.arange(0, len(data['全磷']), 1)
        x2 = np.arange(0, len(data['有效磷']), 1)
        x3 = np.arange(0, len(data['有效磷']/data['全磷']), 1)
        #data1 = data.sort_values(by='全磷', ascending=True)
        #data2 = data.sort_values(by='有效磷', ascending=True)
        data['有效磷占比'] = data['有效磷']/10*data['全磷']
        #data3 = data.sort_values(by='有效磷占比', ascending=True)
        y1 = data['全磷']
        y2 = data['有效磷']
        y3 = data['有效磷占比']
        # getImg(x1, y1,'全磷(g/kg)')
        # getImg(x2, y2, '有效磷(mg/kg)')
        # getImg(x3, y3, '有效磷占全磷比(%)')
        getInteractiveImg(x1, y1,'全磷(g/kg)',[], [], '', [], [], '', url,
                          '全磷分布图', '样品序号', '全磷(g/kg)', data['原样品编号'])
        getInteractiveImg(x2, y2, '有效磷(mg/kg)', [], [], '', [], [], '', url,
                          '有效磷分布图', '样品序号', '有效磷(mg/kg)', data['原样品编号'])
        getInteractiveImg(x3, y3, '有效磷占全磷比(%)', [], [], '', [], [], '', url,
                          '有效磷占全磷比分布图', '样品序号', '有效磷占全磷比(%)', data['原样品编号'])
        del data['有效磷占比']
    return abnormalData



# 14. 全钾、速效钾和缓效钾,绘图
def getKData(data, url):
    data = data.replace(np.nan,0)
    x1 = np.arange(0, len(data['全钾']), 1)
    y1 = data['全钾']
    x2 = np.arange(0, len(data['速效钾'] + data['缓效钾']), 1)
    y2 = data['速效钾']/1000 + data['缓效钾']/1000

    getInteractiveImg(x1, y1, '全钾', x2, y2,'速效钾与缓效钾之和', [],[],'', url,'全钾与速效钾缓效钾之和关系统计图','样品序号','g/kg', data['原样品编号'])
    x = np.arange(0, len(data['速效钾']), 1)
    y = data['速效钾']
    x_1 = np.arange(0, len(data['缓效钾']), 1)
    y_1 = data['缓效钾']
    getInteractiveImg(x, y, '速效钾', x_1, y_1, '缓效钾', [], [], '', url,
                      '速效钾与缓效钾散点图', '样品序号', 'mg/kg', data['原样品编号'])


# 15.重金属 已有 提取重金属异常数据即可
def getMetal(simpleData):
    # resData_14 数据中提取重金属超标的数据,提取相应的指标形成表格
    resData = ''
    return resData
# 16.阳离子交换量与交换性盐基总量
def cationExchangeCapacity(data, url):
    # 绘图
    x1 = data['阳离子交换量']
    y1 = data['交换性盐基总量']
    fig = go.Figure(data=go.Scatter(
        x=x1,
        y=y1,
        text=data['原样品编号'].to_numpy(),
        mode='markers', name='阳离子交换量与交换性盐基总量相关性散点图',
        marker=dict(
            size=4,  # 点的大小
            color='blue',  # 点的颜色
        ))
    )
    print(3.43)
    # 使用sklearn的LinearRegression进行最小二乘法拟合
    model = LinearRegression()
    model.fit(x1.to_numpy().reshape(-1, 1), y1)
    # 计算拟合直线的斜率和截距
    slope = model.coef_[0]
    intercept = model.intercept_
    r, _ = np.corrcoef(y1, slope * x1 + intercept)
    # 绘制拟合直线
    fig.add_trace(go.Scatter(x=x1, y=slope * x1 + intercept, mode='lines', name='拟合直线'))
    # plt.plot(x, slope * x + intercept, color='red', label='拟合直线',  linewidth=2)
    # 设置图表布局
    fig.update_layout(
        title={
            'text': f"阳离子交换量与交换性盐基总量相关性散点图,y={round(slope, 2)}x + {round(intercept, 2)},R²={round(r[1], 2) ** 2},R={round(r[1],1)}",
            'xanchor': 'center',  # 控制水平对齐,可选'left', 'center', 'right'
            'yanchor': 'bottom',  # 控制垂直对齐,可选'top', 'middle', 'bottom'
            'x': 0.5,  # 控制标题的水平位置,0.5代表中心,可以是小数(相对位置)或整数(像素位置)
            'y': 0.9  # 控制标题的垂直位置,0.9代表底部,可以是小数或整数
        },
        xaxis_title='阳离子交换量(g/kg)',
        yaxis_title='交换性盐基总量(mS/cm)')
    html_file_path = f"{url}/阳离子交换量与交换性盐基总量相关性散点图.html"
    pio.write_html(fig, file=html_file_path, auto_open=False)
    # 同时保存一份图片
    pio.write_image(fig, f"{url}/阳离子交换量与交换性盐基总量相关性散点图.png")


# cationExchangeCapacity('')
# 17.交换性盐基:二者之差 交换性盐基总量cmol(+)/kg 交换性钙镁钠钾之和 区分ph>7.5 和ph值<7.5
def changeCation(data,url):
    hightData = data[data['pH'] > 7.5]
    lowData = data[data['pH'] <= 7.5]
    hightData = hightData.apply(pd.to_numeric, errors="coerce")
    lowData = lowData.apply(pd.to_numeric, errors="coerce")
    x_h = np.arange(0, len(hightData['交换性盐基总量']), 1)

    y_h = hightData['交换性盐基总量']
    y1_h = (hightData['交换性钙'] + hightData['交换性镁'] + hightData['交换性钾'] + hightData['交换性钠'])
    y2_h = (y_h-y1_h)
    # 绘图
    getInteractiveImg(x_h, y_h, '交换性盐基总量', x_h, y1_h, '钙镁钾钠之和', x_h, y2_h, '交换性盐基总量与钙镁钾钠和之差', url,
                      '交换性盐基总量与交换性盐相关关系(pH大于7.5)', '样品序号', 'cmol/kg', hightData['原样品编号'])

    x_l = np.arange(0, len(lowData['交换性盐基总量']), 1)
    y_l = lowData['交换性盐基总量']
    y1_l = (lowData['交换性钙'] + lowData['交换性镁'] + lowData['交换性钾'] + lowData['交换性钠'])
    y2_l = (y_l - y1_l)

    getInteractiveImg(x_l, y_l, '交换性盐基总量', x_l, y1_l, '钙镁钾钠之和', x_l, y2_l,
                      '交换性盐基总量与钙镁钾钠和之差', url,
                      '交换性盐基总量与交换性盐相关关系(pH小于等于7.5)', '样品序号', 'cmol/kg', lowData['原样品编号'])

# 18.水溶性盐总量、电导率、离子总量全盐量分布图,全盐量和电导率相关性分析,水溶性盐与离子总量关系
def manyTypes(data,url):
    # data = data.replace('未检测', np.nan)
    data = data.dropna(subset=['全盐量', '电导率'])

    print(3.41)
    # 全盐量分布图
    x = np.arange(0, len(data['全盐量']), 1)
    data_1 = data.sort_values(by='全盐量', ascending=True)
    y = data_1['全盐量']
    getInteractiveImg(x, y, '全盐量', [], [], '', [], [],
                      '', url,
                      '全盐量分布图', '样品序号', '全盐量(g/kg)', data_1['原样品编号'])
    # 电导率分布图
    # x1 = np.arange(0, len(data['电导率']), 1)
    # y1 = data['电导率'].sort_values()
    # getInteractiveImg(x1, y1, '电导率', [], [], '', [], [],
    #                   '', './img/审核报告图形',
    #                   '电导率分布图', '样品序号', '电导率(mS/cm)', data['原样品编号'], 'fileUrl',
    #                   'loc')
    print(3.42)
    x1 = data['全盐量'].dropna()
    y1 = data['电导率'].dropna()
    print('电导率', y1)
    # plt.scatter(x, y)
    fig = go.Figure(data=go.Scatter(
        x=x1,
        y=y1,
        text=data['原样品编号'].to_numpy(),
        mode='markers', name='全盐量与电导率相关关系',
        marker=dict(
            size=10,  # 点的大小
            color='blue',  # 点的颜色
        ))
    )
    print(3.43)
    # 使用sklearn的LinearRegression进行最小二乘法拟合
    model = LinearRegression()
    model.fit(x1.to_numpy().reshape(-1, 1), y1)
    # 计算拟合直线的斜率和截距
    slope = model.coef_[0]
    intercept = model.intercept_
    r, _ = np.corrcoef(y1, slope * x1 + intercept)
    # 绘制拟合直线
    fig.add_trace(go.Scatter(x=x1, y=slope * x1 + intercept, mode='lines', name='拟合直线'))
    # plt.plot(x, slope * x + intercept, color='red', label='拟合直线',  linewidth=2)
    # 设置图表布局
    fig.update_layout(
                      title={
                          'text':f"全盐量与电导率相关性散点图,y={round(slope, 2)}x + {round(intercept, 2)},R²={round(r[1], 2) ** 2},R={round(r[1], 2)}",
                          'xanchor': 'center',  # 控制水平对齐,可选'left', 'center', 'right'
                          'yanchor': 'bottom',  # 控制垂直对齐,可选'top', 'middle', 'bottom'
                          'x': 0.5,  # 控制标题的水平位置,0.5代表中心,可以是小数(相对位置)或整数(像素位置)
                          'y': 0.9  # 控制标题的垂直位置,0.9代表底部,可以是小数或整数
                      },
                      xaxis_title='全盐量(g/kg)',
                      yaxis_title='电导率(mS/cm)')
    html_file_path = f"{url}/全盐量与电导率相关性散点图.html"
    pio.write_html(fig, file=html_file_path, auto_open=False)
    # 同时保存一份图片
    pio.write_image(fig, f"{url}/全盐量与电导率相关性散点图.png")
    print(3.44)


    # 离子总量 水溶性盐总量及差值
    filterData = data.dropna(subset=['全盐量', '离子总量'])
    x2 = np.arange(0, len(filterData['离子总量']), 1)
    print(x2)
    print(3.441)
    print(filterData['离子总量'])
    y2 = filterData['离子总量']
    print(y2)
    print(3.442)
    y3 = filterData['全盐量']
    print(y3)
    print(3.443)

    y4 = (y2-y3)
    if not filterData.empty:
        #要增加对指标值是否缺失进行判断,都不缺失绘图
        getInteractiveImg(x2, y2, '离子总量', x2, y3, '水溶性盐总量', x2, y4,
                      '离子总量与水溶性盐总量之差', url,
                      '水溶性盐总量与离子总量相关性散点图', '样品数量', '离子总量/水溶性盐总量(g/kg)', data['原样品编号'])