import bisect
import itertools
import math
from typing import TypeVar
import warnings

import numpy as np
import pandas as pd
from scipy import stats

import plotly.graph_objects as go
from plotly.subplots import make_subplots
import plotly.io as pio

from spinorama import logger
from spinorama.constant_paths import MIDRANGE_MIN_FREQ, MIDRANGE_MAX_FREQ
from spinorama.filter_peq import peq_spl
from spinorama.compute_misc import compute_contour
from spinorama.load_misc import sort_angles

pio.templates.default = "plotly_white"

# ratio is 4x3
plot_params_default: dict[str, int | str] = {
    "xmin": 20,
    "xmax": 20000,
    "ymin": -40,
    "ymax": 10,
    "width": 1200,
    "height": 800,
}

# ratio is 2x1
contour_params_default = {
    "xmin": 100,
    "xmax": 20000,
    "width": 1200,
    "height": 800,
}

# ratio is 4x5
radar_params_default = {
    "xmin": 400,
    "xmax": 20000,
    "width": 1000,
    "height": 1200,
}

colors = [
    "#5c77a5",
    "#dc842a",
    "#c85857",
    "#89b5b1",
    "#71a152",
    "#bab0ac",
    "#e15759",
    "#b07aa1",
    "#76b7b2",
    "#ff9da7",
]

uniform_colors = {
    # regression
    "Linear Regression": colors[0],
    "Band ±1.5dB": colors[1],
    "Band ±3dB": colors[1],
    # PIR
    "Estimated In-Room Response": colors[0],
    # spin
    "On Axis": colors[0],
    "Listening Window": colors[1],
    "Early Reflections": colors[2],
    "Sound Power": colors[3],
    "Early Reflections DI": colors[4],
    "Sound Power DI": colors[5],
    # reflections
    "Ceiling Bounce": colors[1],
    "Floor Bounce": colors[2],
    "Front Wall Bounce": colors[3],
    "Rear Wall Bounce": colors[4],
    "Side Wall Bounce": colors[5],
    #
    "Ceiling Reflection": colors[1],
    "Floor Reflection": colors[2],
    #
    "Front": colors[1],
    "Rear": colors[2],
    "Side": colors[3],
    #
    "Total Early Reflection": colors[7],
    "Total Horizontal Reflection": colors[8],
    "Total Vertical Reflection": colors[9],
    # SPL
    "10°": colors[1],
    "20°": colors[2],
    "30°": colors[3],
    "40°": colors[4],
    "50°": colors[5],
    "60°": colors[6],
    "70°": colors[7],
    #
    "500 Hz": colors[1],
    "1000 Hz": colors[2],
    "2000 Hz": colors[3],
    "10000 Hz": colors[4],
    "15000 Hz": colors[5],
}

label_short = {}

# label_short = {
#     # regression
#     "Linear Regression": "Reg",
#     "Band ±1.5dB": "±1.5dB",
#     "Band ±3dB": "±3dB",
#     # PIR
#     "Estimated In-Room Response": "PIR",
#     # spin
#     "On Axis": "ON",
#     "Listening Window": "LW",
#     "Early Reflections": "ER",
#     "Sound Power": "SP",
#     "Early Reflections DI": "ERDI",
#     "Sound Power DI": "SPDI",
#     # reflections
#     "Ceiling Bounce": "CB",
#     "Floor Bounce": "FB",
#     "Front Wall Bounce": "FWB",
#     "Rear Wall Bounce": "RWB",
#     "Side Wall Bounce": "SWB",
#     #
#     "Ceiling Reflection": "CR",
#     "Floor Reflection": "FR",
#     #
#     "Front": "F",
#     "Rear": "R",
#     "Side": "S",
#     #
#     "Total Early Reflection": "TER",
#     "Total Horizontal Reflection": "THR",
#     "Total Vertical Reflection": "TVR",
# }

legend_rank = {
    "On Axis": 0,
    "10°": 10,
    "20°": 20,
    "30°": 30,
    "40°": 40,
    "50°": 50,
    "60°": 60,
    "70°": 70,
    "80°": 80,
    "90°": 90,
    "-10°": -10,
    "-20°": -20,
    "-30°": -30,
    "-40°": -40,
    "-50°": -50,
    "-60°": -60,
    "-70°": -70,
    "-80°": -80,
    "-90°": -90,
}

def generate_xaxis(freq_min=20, freq_max=20000):
    return dict(
        title_text="Frequency (Hz)",
        type="log",
        range=[math.log10(freq_min), math.log10(freq_max)],
        showline=True,
        dtick="D1",
    )

def generate_yaxis_spl(range_min=-40, range_max=10, range_step=1):
    return dict(
        title_text="SPL (dB)",
        range=[range_min, range_max],
        dtick=range_step,
        tickvals=list(range(range_min, range_max + range_step, range_step)),
        ticktext=[
            "{}".format(i) if not i % 5 else " "
            for i in range(range_min, range_max + range_step, range_step)
        ],
        showline=True,
    )

def generate_yaxis_di(range_min=-5, range_max=45, range_step=5):
    tickvals = list(range(range_min, range_max, range_step))
    ticktext = [
        f"{di}" if pos < 5 else "" for pos, di in enumerate(range(range_min, range_max, range_step))
    ]
    # print('DEBUG {} {}'.format(tickvals, ticktext))
    return dict(
        title_text="DI (dB)                                                     ",
        range=[range_min, range_max],
        dtick=range_step,
        tickvals=tickvals,
        ticktext=ticktext,
        showline=True,
    )

def generate_yaxis_angles(angle_min=-180, angle_max=180, angle_step=30):
    return dict(
        title_text="Angle",
        range=[angle_min, angle_max],
        dtick=angle_step,
        tickvals=list(range(angle_min, angle_max + angle_step, angle_step)),
        ticktext=["{}°".format(v) for v in range(angle_min, angle_max + angle_step, angle_step)],
        showline=True,
    )

def common_layout(params):
    orientation = "v"
    if params.get("layout", "") == "compact":
        orientation = "h"

    return dict(
        width=params["width"],
        height=params["height"],
        title=dict(
            x=0.5,
            y=0.99,
            xanchor="center",
            yanchor="top",
        ),
        legend=dict(
            x=0.5,
            y=1.12,  # TODO understand why it needs to be larger than 1
            xanchor="center",
            yanchor="top",
            orientation=orientation,
        ),
        margin={
            "t": 100,
            "b": 10,
            "l": 10,
            "r": 10,
        },
    )

def contour_layout(params):
    orientation = "v"
    if params.get("layout", "") == "compact":
        orientation = "h"

    return dict(
        width=params["width"],
        height=params["height"],
        legend=dict(x=0.5, y=0.95, xanchor="center", orientation=orientation),
        title=dict(
            x=0.5,
            y=0.99,
            xanchor="center",
            yanchor="top",
        ),
        margin={
            "t": 40,
            "b": 10,
            "l": 10,
            "r": 10,
        },
        font=dict(
            size=9,
        ),
    )

def radar_layout(params):
    orientation = "v"
    if params.get("layout", "") == "compact":
        orientation = "h"

    return dict(
        width=params["width"],
        height=params["height"],
        legend=dict(
            x=0.5,
            y=1.05,
            xanchor="center",
            orientation=orientation,
            title=dict(
                font=dict(
                    size=10,
                ),
            ),
            font=dict(
                size=9,
            ),
        ),
        title=dict(
            x=0.5,
            y=0.98,
            xanchor="center",
            yanchor="top",
        ),
        margin={
            "t": 100,
            "b": 0,
            "l": 50,
            "r": 50,
        },
    )

def plot_spinorama_traces(spin, params):
    layout = params.get("layout", "")
    traces = []
    for measurement in (
        "On Axis",
        "Listening Window",
        "Early Reflections",
        "Sound Power",
    ):
        if measurement not in spin:
            continue
        trace = go.Scatter(
            x=spin.Freq,
            y=spin[measurement],
            marker_color=uniform_colors.get(measurement, "black"),
            name=label_short.get(measurement, measurement),
            hovertemplate="Freq: %{x:.0f}Hz
SPL: %{y:.1f}dB
",
        )
        if layout != "compact":
            trace.name = measurement
            trace.legendgroup = "measurements"
            trace.legendgrouptitle = {"text": "Measurements"}
        traces.append(trace)

    traces_di = []
    for measurement in ("Early Reflections DI", "Sound Power DI"):
        if measurement not in spin:
            continue
        trace = go.Scatter(
            x=spin.Freq,
            y=spin[measurement],
            marker_color=uniform_colors.get(measurement, "black"),
            hovertemplate="Freq: %{x:.0f}Hz
SPL: %{y:.1f}dB
",
        )
        if layout == "compact":
            trace.name = label_short.get(measurement, measurement)
        else:
            trace.name = measurement
            trace.legendgroup = "directivity"
            trace.legendgrouptitle = {"text": "Directivity"}
        traces_di.append(trace)
    return traces, traces_di

def plot_spinorama(spin, params):
    fig = make_subplots(specs=[[{"secondary_y": True}]])
    t_max = 0
    traces, traces_di = plot_spinorama_traces(spin, params)
    for t in traces:
        t_max = max(t_max, np.max(t.y[np.where(t.x < 20000)]))
        fig.add_trace(t, secondary_y=False)

    t_max = 5 + int(t_max / 5) * 5
    t_min = t_max - 50
    # print('T min={} max={}'.format(t_min, t_max))

    di_max = 0
    for t in traces_di:
        di_max = max(di_max, np.max(t.y[np.where(t.x < 20000)]))
        fig.add_trace(t, secondary_y=True)

    di_max = 35 + int(di_max / 5) * 5
    di_min = di_max - 50
    # print('DI min={} max={}'.format(di_min, di_max))

    fig.update_xaxes(generate_xaxis())
    fig.update_yaxes(generate_yaxis_spl(t_min, t_max, 5))
    fig.update_yaxes(generate_yaxis_di(di_min, di_max, 5), secondary_y=True)

    fig.update_layout(common_layout(params))
    return fig

def plot_graph(df, params):
    layout = params.get("layout", "")
    fig = go.Figure()
    for measurement in df:
        if measurement != "Freq":
            trace = go.Scatter(
                x=df.Freq,
                y=df[measurement],
                hovertemplate="Freq: %{x:.0f}Hz
SPL: %{y:.1f}dB
",
            )
            if layout == "compact":
                trace.name = label_short.get(measurement, measurement)
            else:
                trace.name = measurement
                trace.legendgroup = "measurements"
                trace.legendgrouptitle = {"text": "Measurements"}
            if measurement in uniform_colors:
                trace.marker = {"color": uniform_colors[measurement]}
            if measurement in legend_rank:
                trace.legendrank = legend_rank[measurement]
            fig.add_trace(trace)

    fig.update_xaxes(generate_xaxis())
    fig.update_yaxes(generate_yaxis_spl(params["ymin"], params["ymax"]))
    fig.update_layout(common_layout(params))
    return fig

def plot_graph_spl(df, params):
    layout = params.get("layout", "")
    fig = go.Figure()
    for measurement in df:
        if measurement != "Freq":
            visible = None
            if measurement in ("On Axis", "10°", "20°", "30°", "40°", "50°", "60°"):
                visible = True
            elif measurement in ("-10°", "-20°", "-30°", "-40°", "-50°", "-60°"):
                visible = "legendonly"
            else:
                continue
            trace = go.Scatter(
                x=df.Freq,
                y=df[measurement],
                hovertemplate="Freq: %{x:.0f}Hz
SPL: %{y:.1f}dB
",
                visible=visible,
                showlegend=True,
                legendrank=legend_rank[measurement],
            )
            if layout == "compact":
                trace.name = label_short.get(measurement, measurement)
            else:
                trace.name = measurement
                trace.legendgroup = "measurements"
                trace.legendgrouptitle = {"text": "Measurements"}
            if measurement in uniform_colors:
                trace.marker = {"color": uniform_colors[measurement]}
            if measurement in legend_rank:
                trace.legendrank = legend_rank[measurement]
            fig.add_trace(trace)

    fig.update_xaxes(generate_xaxis())
    fig.update_yaxes(generate_yaxis_spl(params["ymin"], params["ymax"]))
    fig.update_layout(common_layout(params))
    return fig

def plot_graph_traces(df, measurement, params, slope, intercept, line_title):
    layout = params.get("layout", "")
    traces = []

    freq = df.Freq.to_numpy()
    freq_box = np.concatenate([freq, freq[::-1]])
    line = [slope * math.log10(f) + intercept for f in freq]
    line_box30 = np.concatenate([np.add(line, 3.0), np.add(line, -3.0)[::-1]])
    line_box15 = np.concatenate([np.add(line, 1.5), np.add(line, -1.5)[::-1]])

    # some speakers start very high
    restricted_freq = df.loc[(df.Freq >= MIDRANGE_MIN_FREQ) & (df.Freq <= MIDRANGE_MAX_FREQ)]
    restricted_line = [slope * math.log10(f) + intercept for f in restricted_freq.Freq]

    # add 3 dBs zone
    traces.append(
        go.Scatter(
            x=freq_box,
            y=line_box30,
            fill="toself",
            fillcolor="#E2F705",
            line_color="#E2F705",
            opacity=0.25,
            showlegend=True,
            name="Band ±3dB",
        )
    )
    # add 1.5 dBs zone
    traces.append(
        go.Scatter(
            x=freq_box,
            y=line_box15,
            fill="toself",
            fillcolor="#E2F705",
            line_color="#E2F705",
            showlegend=True,
            name="Band ±1.5dB",
        )
    )

    # add line
    showlegend = True
    title = line_title
    if line_title is None:
        showlegend = False
        title = "Linear interpolation"
    traces.append(
        go.Scatter(
            x=freq,
            y=line,
            line=dict(width=2, color="black", dash="dot"),
            opacity=1,
            showlegend=showlegend,
            name=title,
        )
    )

    # add -3/+3 lines
    offset = 3
    # compute the offset as a function of the slope
    # theta = math.atan(slope)
    offset_freq = 0
    offset_spl = offset
    traces.append(
        go.Scatter(
            x=restricted_freq.Freq + offset_freq,
            y=np.array(restricted_line) + offset_spl,
            line=dict(width=2, color="black", dash="dash"),
            opacity=1,
            showlegend=False,
            name="Midrange Band +3dB",
        )
    )
    traces.append(
        go.Scatter(
            x=restricted_freq.Freq + offset_freq,
            y=np.array(restricted_line) - offset_spl,
            line=dict(width=2, color="black", dash="dash"),
            opacity=1,
            showlegend=False,
            name="Midrange Band -3dB",
        )
    )

    trace = go.Scatter(
        x=df.Freq,
        y=df[measurement],
        marker_color=uniform_colors.get(measurement, "black"),
        opacity=1,
        hovertemplate="Freq: %{x:.0f}Hz
SPL: %{y:.1f}dB
",
    )
    if layout == "compact":
        trace.name = label_short.get(measurement, measurement)
    else:
        trace.name = measurement
        trace.legendgroup = "measurements"
        trace.legendgrouptitle = {"text": "Measurements"}
    traces.append(trace)

    return traces

def plot_graph_flat_traces(df, measurement, params):
    restricted_freq = df.loc[(df.Freq >= MIDRANGE_MIN_FREQ) & (df.Freq <= MIDRANGE_MAX_FREQ)]
    slope = 0
    intercept = (
        np.mean(restricted_freq[measurement]) if not restricted_freq[measurement].empty else 0.0
    )
    return plot_graph_traces(df, measurement, params, slope, intercept, None)

def plot_graph_regression_traces(df, measurement, params):
    restricted_freq = df.loc[(df.Freq >= MIDRANGE_MIN_FREQ) & (df.Freq <= MIDRANGE_MAX_FREQ)]
    slope, intercept, _, _, _ = stats.linregress(
        x=np.log10(restricted_freq["Freq"]), y=restricted_freq[measurement]
    )
    return plot_graph_traces(df, measurement, params, slope, intercept, "Midrange ±3dB")

def plot_graph_flat(df, measurement, params):
    params.get("layout", "")
    fig = go.Figure()
    traces = plot_graph_flat_traces(df, measurement, params)
    for t in traces:
        fig.add_trace(t)

    fig.update_xaxes(generate_xaxis())
    fig.update_yaxes(generate_yaxis_spl(params["ymin"], params["ymax"]))

    fig.update_layout(common_layout(params))
    fig.update_traces(mode="lines")

    return fig

def plot_graph_regression(df, measurement, params):
    params.get("layout", "")
    fig = go.Figure()
    traces = plot_graph_regression_traces(df, measurement, params)
    for t in traces:
        fig.add_trace(t)

    fig.update_xaxes(generate_xaxis())
    fig.update_yaxes(generate_yaxis_spl(params["ymin"], params["ymax"]))

    fig.update_layout(common_layout(params))
    fig.update_traces(mode="lines")

    return fig

T = TypeVar("T")

def flatten(l: list[list[T | None]]) -> list[T | None]:
    return list(itertools.chain.from_iterable(l))

def plot_contour(spl, params):
    df_spl = spl.copy()
    params.get("layout", "")
    min_freq = params.get("contour_min_freq", 100)

    contour_start = -30
    contour_end = 3

    contour_colorscale = [
        [0, "rgb(0,0,168)"],
        [0.1, "rgb(0,0,200)"],
        [0.2, "rgb(0,74,255)"],
        [0.3, "rgb(0,152,255)"],
        [0.4, "rgb(74,255,161)"],
        [0.5, "rgb(161,255,74)"],
        [0.6, "rgb(255,255,0)"],
        [0.7, "rgb(234,159,0)"],
        [0.8, "rgb(255,74,0)"],
        [0.9, "rgb(222,74,0)"],
        [1, "rgb(253,14,13)"],
    ]

    fig = go.Figure()

    af, am, az = compute_contour(df_spl.loc[df_spl.Freq > min_freq])
    az = np.clip(az, contour_start, contour_end)
    fig.add_trace(
        go.Contour(
            x=af[0],
            y=am.T[0],
            z=az,
            zmin=contour_start,
            zmax=contour_end,
            contours=dict(
                coloring="fill",
                start=contour_start + 0,
                end=contour_end - 0,
                size=3,
                showlines=True,
                # showlabels=True,
            ),
            colorbar=dict(
                dtick=3,
                len=1.0,
                lenmode="fraction",
            ),
            autocolorscale=False,
            colorscale=contour_colorscale,
            hovertemplate="Freq: %{x:.0f}Hz
Angle: %{y:.0f}
SPL: %{z:.1f}dB
",
        )
    )

    def add_lines(x, y):
        fig.add_trace(
            go.Scatter(
                x=x,
                y=y,
                opacity=0.5,
                marker_color="white",
                line_width=1,
                showlegend=False,
            )
        )

    def compute_horizontal_lines(
        x_min: float, x_max: float, y_data: range
    ) -> tuple[list[float | None], list[int | None]]:
        x = [x_min, x_max, None] * len(y_data)
        y = flatten([[a, a, None] for a in y_data])
        return x, y

    def compute_vertical_lines(
        y_min: int, y_max: int, x_data: list[int]
    ) -> tuple[list[int | None], list[float | None]]:
        x = flatten([[a, a, None] for a in x_data])
        y = [y_min, y_max, None] * len(x_data)
        return x, y

    hx, hy = compute_horizontal_lines(min_freq, 20000, range(-150, 180, 30))
    vrange = (
        [100 * i for i in range(2, 9)]
        + [1000 * i for i in range(1, 10)]
        + [10000 + 1000 * i for i in range(1, 9)]
    )
    vx, vy = compute_vertical_lines(-180, 180, vrange)

    add_lines(hx, hy)
    add_lines(vx, vy)

    fig.update_xaxes(generate_xaxis(min_freq))
    fig.update_yaxes(generate_yaxis_angles())
    fig.update_yaxes(
        zeroline=True,
        zerolinecolor="#000000",
        zerolinewidth=3,
    )
    fig.update_layout(contour_layout(params))
    return fig

def find_nearest_freq(dfu: pd.DataFrame, hz: float, tolerance: float = 0.05) -> int | None:
    """return the index of the nearest freq in dfu, return None if not found"""
    ihz = None
    for i in dfu.index:
        f = dfu.loc[i]
        if abs(f - hz) < hz * tolerance:
            ihz = i
            break
    if ihz:
        logger.debug("nearest: %.1f hz at loc %d", hz, ihz)
    return ihz

def plot_radar(spl, params):
    layout = params.get("layout", "")

    anglelist = list(range(-180, 180, 10))

    def projection(anglelist, grid_z, hz):
        dbs_r = [db for a, db in zip(anglelist, grid_z, strict=False)]
        dbs_theta = [a for a, db in zip(anglelist, grid_z, strict=False)]
        dbs_r.append(dbs_r[0])
        dbs_theta.append(dbs_theta[0])
        return dbs_r, dbs_theta, [hz for i in range(0, len(dbs_r))]

    def label(i):
        return "{:d} Hz".format(i)

    def plot_radar_freq(anglelist, freqlist, df):
        dfu = sort_angles(df)
        db_mean = dfu.loc[(dfu.Freq > 900) & (dfu.Freq < 1100)]["On Axis"].mean()
        freq = dfu.Freq
        dfu = dfu.drop("Freq", axis=1)
        db_min = np.min(dfu.min(axis=0).values)
        db_max = np.max(dfu.max(axis=0).values)
        max(abs(db_max), abs(db_min))
        # if df is normalized then 0 will be at the center of the radar which is not what
        # we want. Let's shift the whole graph up.
        # if db_mean < 45:
        #    dfu += db_scale
        # print(db_min, db_max, db_mean, db_scale)
        # build 3 plots
        db_x = []
        db_y = []
        hz_z = []
        for hz in freqlist:
            ihz = find_nearest_freq(freq, hz)
            if ihz is None:
                continue
            p_x, p_y, p_z = projection(anglelist, dfu.loc[ihz][dfu.columns != "Freq"], hz)
            # add to global variable
            db_x.append(p_x)
            db_y.append(p_y)
            hz_z.append(p_z)

        # normalise
        db_x = [v2 for v1 in db_x for v2 in v1]
        db_y = [v2 for v1 in db_y for v2 in v1]
        # print("db_x min={} max={}".format(np.array(db_x).min(), np.array(db_x).max()))
        # print("db_y min={} max={}".format(np.array(db_y).min(), np.array(db_y).max()))

        hz_z = [label(i2) for i1 in hz_z for i2 in i1]

        return db_mean, pd.DataFrame({"R": db_x, "Theta": db_y, "Freq": hz_z})

    fig = make_subplots(
        rows=2,
        cols=2,
        specs=[
            [{"type": "polar"}, {"type": "polar"}],
            [{"type": "polar"}, {"type": "polar"}],
        ],
        horizontal_spacing=0.15,
        vertical_spacing=0.05,
    )

    radialaxis = dict(
        range=[-45, 5],
        dtick=5,
        tickfont=dict(
            size=10,
        ),
    )
    angularaxis = dict(
        dtick=10,
        tickvals=list(range(0, 360, 10)),
        ticktext=[
            f"{x}°" if abs(x) < 60 or not x % 30 else " "
            for x in (list(range(0, 190, 10)) + list(range(-170, 0, 10)))
        ],
        tickfont=dict(
            size=10,
        ),
    )

    radar_colors = {
        "100 Hz": colors[0],
        "125 Hz": colors[1],
        "160 Hz": colors[2],
        "200 Hz": colors[3],
        "250 Hz": colors[4],
        "315 Hz": colors[5],
        "400 Hz": colors[6],
        "500 Hz": colors[7],
        "1600 Hz": colors[8],
        "2000 Hz": colors[9],
        "2500 Hz": colors[0],
        "3150 Hz": colors[1],
        "4000 Hz": colors[2],
        "5000 Hz": colors[3],
        "6000 Hz": colors[4],
        "8000 Hz": colors[5],
    }

    def update_pict(anglelist, freqlist, row, col, spl):
        _, dbs_df = plot_radar_freq(anglelist, freqlist, spl)

        for freq in np.unique(dbs_df["Freq"].values):
            mslice = dbs_df.loc[dbs_df.Freq == freq]
            trace = go.Scatterpolar(
                r=mslice.R,
                theta=mslice.Theta,
                dtheta=30,
                name=freq,
                marker_color=radar_colors.get(freq, "black"),
                legendrank=int(freq[:-3]),
            )
            if layout != "compact":
                trace.legendgroup = ("measurements",)
                trace.legendgrouptitle_text = ("Frequencies",)
            fig.add_trace(trace, row=row, col=col)
            fig.update_polars(radialaxis=radialaxis, angularaxis=angularaxis, row=row, col=col)

    update_pict(anglelist, [100, 125, 160, 200], 1, 1, spl)
    update_pict(anglelist, [1600, 2000, 2500, 3150], 1, 2, spl)
    update_pict(anglelist, [250, 315, 400, 500], 2, 1, spl)
    update_pict(anglelist, [4000, 5000, 6300, 8000], 2, 2, spl)

    fig.update_layout(radar_layout(params))
    # fig.update_layout(polar=dict(radialaxis=radialaxis, angularaxis=angularaxis)), row=row, col=col)

    return fig

def plot_image(df, params):
    return None

def plot_summary(df, summary, params):
    return None

def plot_eqs(freq, peqs, names):
    peqs_spl = [peq_spl(freq, peq) for peq in peqs]
    if len(peqs) > 1:
        freq_min = bisect.bisect_right(freq, 80)
        freq_max = bisect.bisect_left(freq, 3000)
        with warnings.catch_warnings():
            warnings.simplefilter("ignore", RuntimeWarning)
            if freq_min < freq_max:
                peqs_restriced = [np.array(spl)[freq_min:freq_max] for spl in peqs_spl]
                peqs_avg = [np.mean(v) if len(v) > 0 else 0.0 for v in peqs_restriced]
                peqs_spl = [
                    np.array(spl) - (peqs_avg[i] - peqs_avg[0]) for i, spl in enumerate(peqs_spl)
                ]
    traces = None
    if names is None:
        traces = [go.Scatter(x=freq, y=spl) for spl in peqs_spl]
    else:
        traces = [
            go.Scatter(
                x=freq,
                y=spl,
                name=name,
                hovertemplate="Freq: %{x:.0f}Hz
SPL: %{y:.1f}dB
",
            )
            for spl, name in zip(peqs_spl, names, strict=False)
        ]
    fig = go.Figure(data=traces)
    fig.update_xaxes(
        dict(
            title_text="Frequency (Hz)",
            type="log",
            range=[math.log10(20), math.log10(20000)],
            showline=True,
            dtick="D1",
        ),
    )
    fig.update_yaxes(
        dict(
            title_text="SPL (dB)",
            range=[-5, 5],
            showline=True,
            dtick="D1",
        ),
    )
    fig.update_layout(
        title="EQs",
        width=600,
        height=450,
        legend=dict(
            orientation="h",
            yanchor="bottom",
            xanchor="center",
            y=1.1,
            x=0.5,
            title=None,
            font=dict(size=12),
        ),
    )
    return fig

def plot_contour_3d(spl, params):
    params.get("layout", "")
    min_freq = max(20, params.get("contour_min_freq", 100))

    contour_start = -30
    contour_end = 3

    contour_colorscale = [
        [0, "rgb(0,0,168)"],
        [0.1, "rgb(0,0,200)"],
        [0.2, "rgb(0,74,255)"],
        [0.3, "rgb(0,152,255)"],
        [0.4, "rgb(74,255,161)"],
        [0.5, "rgb(161,255,74)"],
        [0.6, "rgb(255,255,0)"],
        [0.7, "rgb(234,159,0)"],
        [0.8, "rgb(255,74,0)"],
        [0.9, "rgb(222,74,0)"],
        [1, "rgb(253,14,13)"],
    ]

    colorbar = dict(
        dtick=3,
        len=0.5,
        lenmode="fraction",
        tickfont=dict(size=14),
    )

    angle_list_3d = [-180, -150, -120, -90, -60, -30, 0, 30, 60, 90, 120, 150, 180]
    angle_text_3d = [f"{a}°" for a in angle_list_3d]
    spl_list_3d = [0, -5, -10, -15, -20, -25, -30, -35, -40, -45]
    spl_text_3d = [f"{s}" if s > -45 else "" for s in spl_list_3d]

    def a2v(angle):
        if angle == "Freq":
            return -1000
        elif angle == "On Axis":
            return 0
        iangle = int(angle[:-1])
        return iangle

    def transform(spl, db_max, clip_min, clip_max):
        if "-180°" not in spl and "180°" in spl:
            spl["-180°"] = spl["180°"]
        df_spl = spl.reindex(columns=sorted(spl.columns, key=lambda a: a2v(a))) - db_max
        # x,y,z
        freq = df_spl.Freq
        angle = [a2v(i) for i in df_spl.loc[:, df_spl.columns != "Freq"].columns]
        selector = (df_spl["Freq"] > min_freq) & (df_spl["Freq"] < 20000)
        spl = df_spl.loc[selector, df_spl.columns != "Freq"].T.to_numpy()
        # color
        color = np.clip(np.multiply(np.floor_divide(spl, 3), 3), clip_min, clip_max)
        return freq, angle, spl, color

    db_max = spl["On Axis"].max()

    freqs, angles, spls, colors = transform(spl, db_max, contour_start, contour_end)

    fig = go.Figure()

    trace = go.Surface(
        x=freqs,
        y=angles,
        z=spls,
        showscale=True,
        autocolorscale=False,
        colorscale=contour_colorscale,
        surfacecolor=colors,
        colorbar=colorbar,
        cmin=contour_start,
        cmax=contour_end,
        hovertemplate="Freq: %{x:.0f}Hz
Angle:  %{y}°
 SPL: %{z:.1f}dB
",
    )

    fig.add_trace(trace)

    fig.update_layout(
        autosize=False,
        width=600,
        height=700,
        scene=dict(
            xaxis=dict(
                title="Freq. (Hz)",
                tickfont=dict(
                    size=11,
                ),
                titlefont=dict(
                    size=14,
                ),
            ),
            yaxis=dict(
                range=[-180, 180],
                showline=True,
                tickvals=angle_list_3d,
                ticktext=angle_text_3d,
                title="Angle",
                tickfont=dict(
                    size=11,
                ),
                titlefont=dict(
                    size=14,
                ),
            ),
            zaxis=dict(
                range=[-45, 5],
                title="SPL",
                showline=True,
                tickvals=spl_list_3d,
                ticktext=spl_text_3d,
                tickfont=dict(
                    size=11,
                ),
                titlefont=dict(
                    size=14,
                ),
            ),
        ),
    )
    fig.update_traces(contours_z=dict(show=True, project_z=True))

    return fig


#!/usr/bin/env python
# coding=utf-8
from torchvision.ops import MultiScaleRoIAlign
from torchvision.ops import roi_align

class NewMultiScaleRoIAlign(MultiScaleRoIAlign):

    def __init__(self, featmap_names, output_size, sampling_ratio):
        super(NewMultiScaleRoIAlign, self).__init__(
            featmap_names, output_size, sampling_ratio
        )

    def forward(self, x, boxes, image_shapes):
        x_filtered = []
        for k, v in x.items():
            if k in self.featmap_names:
                x_filtered.append(v)
        rois = self.convert_to_roi_format(boxes)
        if self.scales is None:
            self.setup_scales(x_filtered, image_shapes)

        scales = self.scales
        assert scales is not None
        result = []
        for level, (per_level_feature, scale) in enumerate(
            zip(x_filtered, scales)
        ):
            this_level_out = roi_align(
                per_level_feature, rois,
                output_size=self.output_size,
                spatial_scale=scale, sampling_ratio=self.sampling_ratio
            )
            result.append(this_level_out)
        return result


from django import forms
#from tinymce.widgets import TinyMCE
from ckeditor.widgets import CKEditorWidget
from .models import Post, Comment, CustomUser, ContactModel
from django.contrib.auth.forms import UserCreationForm, UserChangeForm

class CustomUserCreationForm(UserCreationForm):

    class Meta:
        model = CustomUser
        fields = ('username', 'email')

class SignupForm(forms.Form):
    first_name = forms.CharField(max_length=30)
    last_name = forms.CharField(max_length=30)
    age = forms.IntegerField(max_value=100)

    class Meta:
        model = CustomUser

    def save(self, user):
        user.first_name = self.cleaned_data['first_name']
        user.last_name = self.cleaned_data['last_name']
        user.age = self.cleaned_data['age']
        user.save()

class CustomUserChangeForm(UserChangeForm):

    class Meta:
        model = CustomUser
        fields = ('username', 'email')

"""class TinyMCEWidget(TinyMCE):
    def use_required_attribute(self, *args):
        return False"""

class PostForm(forms.ModelForm):
    #content = forms.CharField(widget=TinyMCE(mce_attrs={'width': 800}))
    """content = forms.CharField(
        widget=TinyMCE(
            attrs={'required': False, 'cols': 80, 'rows': 50}
        )
    )"""

    #content = forms.CharField(widget=CKEditorWidget())

    class Meta:
        model = Post
        fields = ('title', 'keywords', 'overview', 'content', 'thumbnail',
        'categories', 'featured', 'previous_post', 'next_post')

class CommentForm(forms.ModelForm):

    """content = forms.CharField(widget=forms.Textarea(attrs={
        'class': 'form-control',
        'placeholder': 'Type your comment',
        'id': 'usercomment',
        'rows': '4',
        'name': 'usercomment',
    }))"""
    class Meta:
        model = Comment
        fields = ('content', 'email', 'name' )
        widgets = {
            "name": forms.TextInput(attrs={"class": "form-control",
                'placeholder': 'Please enter your name',
                'id': 'usercomment',
                'rows': '4',
                'name': 'usercomment'}),
            "email": forms.EmailInput(attrs={"class": "form-control",
                'placeholder': 'Please enter your email',
                'id': 'usercomment',
                'rows': '4',
                'name': 'usercomment'}),
            "content": forms.Textarea(attrs={'class': 'form-control',
                'placeholder': 'Please enter your comment',
                'id': 'usercomment',
                'rows': '4',
                'name': 'usercomment',})
            }

class ContactForm(forms.Form):
    email = forms.EmailField(widget = forms.EmailInput(attrs={"class": "form-control border",
                'placeholder': 'Your email',}), required=False)
    subject = forms.CharField(widget= forms.TextInput(attrs={"class": "form-control border",
                'placeholder': 'Subject of your message',}), required=True)
    message = forms.CharField(widget=forms.Textarea(attrs={'class': 'form-control',
                'placeholder': 'Your message',
                'id': 'usercomment',
                'rows': '4',
                'name': 'usercomment',}), required=True)

    class Meta:
        model = ContactModel
        fields = '__all__'

        """widgets = {
            "subject": forms.TextInput(attrs={"class": "form-control border"}),
            "email": forms.EmailInput(attrs={"class": "form-control border"}),
            "message": forms.Textarea(attrs={"class": "form-control border"})
        }"""

        """widgets = {
            "subject": forms.TextInput(attrs={"class": "form-control border",
                'placeholder': 'Subject of your message',}),
            "email": forms.EmailInput(attrs={"class": "form-control border",
                'placeholder': 'Your email'}),
            "message": forms.Textarea(attrs={'class': 'form-control',
                'placeholder': 'Your message',
                'id': 'contact_message',
                'rows': '4',
                'name': 'contact_message',})
            }"""

class TestCommentForm(forms.ModelForm):

    """content = forms.CharField(widget=forms.Textarea(attrs={
        'class': 'form-control',
        'placeholder': 'Type your comment',
        'id': 'usercomment',
        'rows': '4',
        'name': 'usercomment',
    }))"""
    class Meta:
        model = Comment
        fields = ('content', 'email', 'name' )
        widgets = {
            "name": forms.TextInput(attrs={"class": "form-control border",
                'placeholder': 'Please enter your name',
                'id': 'usercomment',
                'rows': '4',
                'name': 'usercomment'}),
            "email": forms.EmailInput(attrs={"class": "form-control border",
                'placeholder': 'Please enter your email',
                'id': 'usercomment',
                'rows': '4',
                'name': 'usercomment'}),
            "content": forms.Textarea(attrs={'class': 'form-control',
                'placeholder': 'Please enter your comment',
                'id': 'usercomment',
                'rows': '4',
                'name': 'usercomment',})
            }


import random
from contagion_node import Node
from random import randint

class Graph:
    def __init__(self, graphSeedData):
        self.graphSeedData = graphSeedData
        self.graph = {}
        self.initGraph()

    # create a graph of x size and with y probability that each graph are connected
    def initGraph(self):
        for val in self.graphSeedData:
            self.addVert(val)
        for val in self.graphSeedData:
            for edge in self.graphSeedData[val]:
                self.addEdge(val, edge)

    # graph (aka vertexes): {1 : [2], 2:[1], 3:[]} etc...
    def addVert(self, val):
        self.graph[val] = Node(val)

    def addEdge(self, node1, node2):
        self.graph[node1].edges.add(node2)
        self.graph[node2].edges.add(node1)

    def infectEarlyAdopters(self, earlyAdoptersSet, flippedChoice="X", startingChoice="Y"):
        for node in self.graph.values():
            node.choice = startingChoice
        for val in earlyAdoptersSet:
            self.graph[val].choice = flippedChoice

    def flipOrStay(self, q):
        flipped = False
        for node in self.graph.values():
            x = 0
            y = 0
            if node.choice != 'X':
                for neighbor in node.edges:
                    if self.graph[neighbor].choice == 'X':
                        x += 1
                    else:
                        y += 1
            if (x + y) > 0 and x/ float(x + y) > q:
                node.choice = 'X'
                flipped = True
        return flipped

    def brd(self, q):
        while self.flipOrStay(q):
            continue
        return (self.countChoice('X'), self.countChoice('Y'))

    def findAverageBrd(self, numOfIterations, threshold, numOfEarlyAdopters):
        # k = set of early adopters
        totalX = 0
        totalY = 0
        for i in range(0, numOfIterations):
            k = self.createEarlyAdopterSet(numOfEarlyAdopters) if type(numOfIterations) is int else numOfEarlyAdopters
            self.infectEarlyAdopters(k)
            choices = self.brd(threshold)
            totalX += choices[0]
            totalY += choices[1]
        return 'Infection Rate: {}% X: {} Y: {} Q: {} K: {} Num of Tests: {}'.format(round((totalX/float((totalX + totalY))) * 100, 2), totalX/numOfIterations, totalY/numOfIterations, threshold, numOfEarlyAdopters, numOfIterations)
        # print(float(totalX/numOfIterations), float(totalY/numOfIterations))

    def createEarlyAdopterSet(self, num):
        randomSet = []
        for i in range(0, num):
            x = randint(0, 4038)
            randomSet.append(x)
        return set(randomSet)

    def countChoice(self, choice):
        return [x.choice for x in self.graph.values()].count(choice)

    def printGraph(self):
        print(self.graph)

    def getGraph(self):
        return self.graph


import pygame
from pygame.sprite import Sprite
import random
from vector import Vector

class Asteroid(Sprite):
    """A class to respresent a single asteroid"""

    def __init__(self, as_settings, screen, size, x, y):
        """Initialize the asteroid and its starting position"""
        super(Asteroid, self).__init__()
        self.screen = screen
        self.as_settings = as_settings
        self.size = size

        #Load a random asteroid image and set its rect properties
        self.image = pygame.image.load('images/asteroid{0}.png'.format(random.choice(range(1, 4))))
        self.rect = self.image.get_rect()
        self.image = pygame.transform.scale(self.image, (self.rect.width * self.size, self.rect.height * self.size))
        self.mask = pygame.mask.from_surface(self.image)

        self.rect.centerx = x
        self.rect.centery = y
        self.velocity = Vector(self.as_settings.asteroid_speed_factor, random.randint(0, 359))

        #Store the asteroid's exact position
        self.center = [self.rect.centerx, self.rect.centery]

        if not self.size:
            self.size = self.as_settings.num_splits

    def blitme(self):
        """Draw the asteroid at its current location"""
        self.screen.blit(self.image, self.rect)

    def update(self):
        """Move the asteroid according to its velocity"""
        vel_components = self.velocity.components()
        self.center = [round(self.center[0] + vel_components[0], 2), round(self.center[1] + vel_components[1], 2)]
        if self.center[0] > self.as_settings.screen_width:
            self.center[0] = 0
        if self.center[0] < 0:
            self.center[0] = self.as_settings.screen_width
        if self.center[1] > self.as_settings.screen_height:
            self.center[1] = 0
        if self.center[1] < 0:
            self.center[1] = self.as_settings.screen_height
        self.rect.center = self.center
        self.mask = pygame.mask.from_surface(self.image, 0)

    def split(self, asteroids):
        if self.size == 1:
            asteroids.remove(self)
        else:
            asteroids.remove(self)
            for i in range(random.randint(2, 4)):
                new_asteroid = Asteroid(self.as_settings, self.screen, self.size - 1, self.center[0], self.center[1])
                asteroids.add(new_asteroid)


import re
import logging
from virttest import utils_config
from virttest import utils_libvirtd
from autotest.client import utils
from autotest.client.shared import error

def run(test, params, env):
    """
    Test seccomp_sandbox parameter in qemu.conf.

    1) Change seccomp_sandbox in qemu.conf;
    2) Restart libvirt daemon;
    3) Check if libvirtd successfully started;
    4) Check if qemu command line changed accordingly;
    """
    def get_qemu_command_sandbox_option(vm):
        """
        Get the sandbox option of qemu command line of a libvirt VM.

        :param vm: A libvirt_vm.VM class instance.
        :return :  A string containing '-sandbox' option of VM's qemu command
                   line or None if not found.
        """
        if vm.is_dead():
            vm.start()

        # Get qemu command line.
        pid = vm.get_pid()
        res = utils.run("ps -p %s -o cmd h" % pid)

        if res.exit_status == 0:
            match = re.search(r'-sandbox\s*(\S*)', res.stdout)
            if match:
                return match.groups()[0]

    vm_name = params.get("main_vm", "virt-tests-vm1")
    expected_result = params.get("expected_result", "not_set")
    seccomp_sandbox = params.get("seccomp_sandbox", "not_set")
    vm = env.get_vm(vm_name)

    # Get old qemu -sandbox option.
    orig_qemu_sandbox = get_qemu_command_sandbox_option(vm)
    logging.debug('Original "-sandbox" option of qemu command is '
                  '"%s".' % orig_qemu_sandbox)

    config = utils_config.LibvirtQemuConfig()
    libvirtd = utils_libvirtd.Libvirtd()
    try:
        if seccomp_sandbox == 'not_set':
            del config.seccomp_sandbox
        else:
            config.seccomp_sandbox = seccomp_sandbox

        # Restart libvirtd to make change valid.
        if not libvirtd.restart():
            if expected_result != 'unbootable':
                raise error.TestFail('Libvirtd is expected to be started '
                                     'with seccomp_sandbox = '
                                     '%s' % seccomp_sandbox)
            return
        if expected_result == 'unbootable':
            raise error.TestFail('Libvirtd is not expected to be started '
                                 'with seccomp_sandbox = '
                                 '%s' % seccomp_sandbox)

        # Restart VM to create a new qemu command line.
        if vm.is_alive():
            vm.destroy()
        vm.start()

        # Get new qemu -sandbox option.
        new_qemu_sandbox = get_qemu_command_sandbox_option(vm)
        logging.debug('New "-sandbox" option of qemu command is '
                      '"%s"' % new_qemu_sandbox)

        if new_qemu_sandbox is None:
            if expected_result != 'not_set':
                raise error.TestFail('Qemu sandbox option is expected to set '
                                     'but %s found', new_qemu_sandbox)
        else:
            if expected_result != new_qemu_sandbox:
                raise error.TestFail('Qemu sandbox option is expected to be '
                                     '%s, but %s found' % (
                                         expected_result, new_qemu_sandbox))
    finally:
        config.restore()
        libvirtd.restart()


import unittest

from src import utils
from src.constrained_min import interior_pt
from tests import examples
import numpy as np

class TestMinimizer(unittest.TestCase):

    def test_qp(self):
        f = examples.Function1()
        x0 = [0.1, 0.2, 0.7]
        x_list,objectives = interior_pt(f.func, f.ineq_constraints, f.eq_constraints_matrix, f.eq_constraints_res, x0)
        x = x_list[-1]
        inq_constraint = []
        for i in f.ineq_constraints:
            res = i(x)[0]
            inq_constraint.append(res)
        print("quadratic programming")
        print(" * final point is: ", x)
        print(" * final objective is: ", f.func(x)[0])
        print(" * final inequality constraints: ", inq_constraint)
        print(" * final equality constraints: ", np.dot(f.eq_constraints_matrix, x))

        utils.plot_result_3d(x_list,angle=(5,30))
        utils.plot_result_3d(x_list,angle=(50,45))
        utils.plot_value_iterations(objectives, name_of_function="quadratic programming")

    def test_lp(self):
        f = examples.Function2()
        x0 = [0.5, 0.75]
        x_list, objectives = interior_pt(f.func, f.ineq_constraints, f.eq_constraints_matrix, f.eq_constraints_res, x0)
        x = x_list[-1]
        inq_constraint = []
        for i in f.ineq_constraints:
            res = i(x)[0]
            inq_constraint.append(res)
        print("linear programming")
        print(" * final point is: ", x)
        print(" * final objective is: ", f.func(x)[0])
        print(" * final inequality constraints: ", inq_constraint)
        print(" * final equality constraints: ", None)
        utils.plot_result(x_list)
        utils.plot_value_iterations(-1*np.array(objectives), name_of_function="linear programing")


from tkinter import *

# Conversion function
def convert():
    # Convert from miles to kilometers
    if mode.get() == "Miles to Km":
        miles = float(miles_input.get())
        kilometers = miles * 1.60934
        result_label["text"] = f"{kilometers:.4f}"
    # Convert from kilometers to miles
    else:
        kilometers = float(miles_input.get())
        miles = kilometers / 1.60934
        result_label["text"] = f"{miles:.4f}"

# Toggle between conversion modes
def toggle_conversion():
    # Switch to Km to Miles mode
    if mode.get() == "Miles to Km":
        mode.set("Km to Miles")
        input_label.config(text="Km")
        result_unit_label.config(text="Miles")
    # Switch to Miles to Km mode
    else:
        mode.set("Miles to Km")
        input_label.config(text="Miles")
        result_unit_label.config(text="Km")
    # Reset input and result fields
    result_label["text"] = "0"
    miles_input.delete(0, END)
    miles_input.insert(END, string="0")

# Set up GUI
window = Tk()
window.title("Unit Converter")
window.config(padx=30, pady=30)

mode = StringVar(value="Miles to Km")

# Input field for distance
miles_input = Entry(width=10)
miles_input.insert(END, string="0")
miles_input.grid(column=1, row=0, padx=5, pady=5)

# Label for input unit
input_label = Label(text="Miles")
input_label.grid(column=2, row=0, padx=5, pady=5)

# Label for conversion equality
equal_to_label = Label(text="is equal to")
equal_to_label.grid(column=0, row=1, padx=5, pady=5)

# Label for conversion result
result_label = Label(text="0")
result_label.grid(column=1, row=1, padx=5, pady=5)

# Label for result unit
result_unit_label = Label(text="Km")
result_unit_label.grid(column=2, row=1, padx=5, pady=5)

# Button for triggering conversion
calculate_button = Button(text="Calculate", command=convert)
calculate_button.grid(column=1, row=2)

# Button for toggling conversion mode
toggle_button = Button(text="Toggle Conversion", command=toggle_conversion)
toggle_button.grid(column=1, row=3)

window.mainloop()


# -*- coding: utf-8 -*-

from bottle import (
	route,
	run,
	template,
	view,
	request,
	post,
	get,
	redirect,
	abort
)

import redis, random
import datetime, time
import json
import func, mail, mail_deal

r = redis.StrictRedis()

@route('/')
@view('templates/test')
def main():
	# 判断是否按访问量排序，0否
	order_by = request.query.order or '0'
	# 当前页数
	page = int(request.query.page or '1')
	# 总页数
	pages = 5

	start = (page - 1) * pages
	end = page * pages - 1
	if order_by == '0':
		pages_id_bytes = r.lrange('posts:list', start, end)
	else:
		pages_id_bytes = r.zrevrange('posts:visited', start, end)
	pages_id = func.bytes_to_string_list(pages_id_bytes)

	post_list = {}
	visit_time = {}
	for id in pages_id:
		# 根据每个页数获取的数据
		d = r.hgetall("{}".format(id))
		data = func.bytes_to_string_dict(d)
		# 字典无序
		post_list[id] = data
		visit_time[id] = r.zscore('posts:visited', id)
	# 排序
	new_post_list = sorted(post_list.items(), reverse=True)

	return dict(posts=new_post_list, page=page, visit_time=visit_time)

@route('/send')
@view('templates/send')
def send():
	return dict()

@post('/result')
@view('templates/send')
def deal():
	article_id = r.hincrby("myarticle", 'id', 1)
	title = request.forms.getunicode("title")
	articles = request.forms.getunicode("articles")
	slug = request.forms.getunicode("slug")
	name = request.forms.getunicode("name")
	submit_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')

	# 建立slug和post_id的对应关系
	if not r.hset('slug.to.id', slug, article_id):
		return dict(tips='slug already exists')

	r.hmset("{}".format(article_id), {'title':title,
	'articles':articles, 'name':name, 'time':submit_time, 'slug':slug
	})
	# 文章列表分页
	r.lpush('posts:list', article_id)

	subscribe_users = func.bytes_to_string_list(r.smembers('subscribe'))
	url = 'http://192.168.1.130:1213/{}'.format(slug)
	for user in subscribe_users:
		# mail.send_email(user, url, 'post')
		email_info = {
			'email':user,
			'url':url,
			'tpl':'post'
		}
		r.lpush('queue:notify.email', json.dumps(email_info))

	# 重定向到对应id的文章
	redirect('/{}'.format(slug))

@route('/<slug>')
@view('templates/result')
def result(slug):
	id = r.hget('slug.to.id', slug)
	if not id:
		abort(404, '文章不存在')
	id = id.decode('utf-8')

	# visit_time = r.hincrby("myarticle", 'p{}'.format(id), 1)
	visit_time = int(r.zincrby('posts:visited', 1, id))

	title = r.hget("{}".format(id), 'title')
	articles = r.hget("{}".format(id), 'articles')
	name = r.hget("{}".format(id), 'name')
	time = r.hget("{}".format(id), 'time')

	tags = func.bytes_to_string_list(r.smembers('post:{}:tags'.format(id)))

	return dict(title=title, articles=articles, name=name
		, time=time, visit_time=visit_time, id=id, tags=tags)

@post('/tags/add')
def add_tag():
	pid = request.forms.getunicode("p_id")
	tag = request.forms.getunicode('tag')

	r.sadd('post:{}:tags'.format(pid), tag)
	r.sadd('tag:{}:posts'.format(tag), pid)

	# 用list方便遍历
	tags = func.bytes_to_string_list(r.smembers('post:{}:tags'.format(pid)))
	# 返json
	return {'tags':tags}

@route('/tag/list/<tag>')
@view('templates/showtag')
def tag_list(tag):
	pid = func.bytes_to_string_list(r.smembers('tag:{}:posts'.format(tag)))

	post_list = {}
	for id in pid:
		d = r.hgetall("{}".format(id))
		data = func.bytes_to_string_dict(d)
		post_list[id] = data
	# 排序
	new_post_list = sorted(post_list.items(), reverse=True)
	return dict(posts=new_post_list, tag=tag)

@post('/pubsub')
@view('active')
def pubsub():
	email = request.forms.getunicode('email').strip()
	# 判断邮件是否重复
	# ...

	samples = "qwertyuioplkjhgfdsazxcvbnm0123456789"
	postfix = ''.join([random.choice(samples) for i in range(10)])
	url = 'http://192.168.1.130:1213/active/{}'.format(postfix)

	# send email
	# mail.send_email(email, url,'subscribe')

	email_info = {
		'email':email,
		'url':url,
		'tpl':'subscribe'
	}
	r.lpush('queue:cofirm.email', json.dumps(email_info))

	pipe = r.pipeline()
	pipe.set(postfix, email)
	pipe.expire(postfix, 7200)
	pipe.execute()

	return dict(email = email)

@get('/active/<postfix>')
@view('subscribe')
def subscribe(postfix):
	if not r.exists(postfix):
		return dict(result = '激活链接不存在或已过期，请重新订阅')
	email = r.get(postfix).decode('utf-8')
	r.sadd('subscribe', email)
	return dict(result='用户订阅地址：{}激活成功，感谢订阅'.format(email))

# 多线程执行
back_mail = mail_deal.MailSend()
back_mail.start()

run(host='0.0.0.0', port=1213, reloader=True)


# -*- coding: utf-8 -*-
"""
Created on Tue May 17 16:00:59 2022

@author: reinhardt
"""

import pandas as pd
import os
import matplotlib.pyplot as plt
import numpy as np

from os.path import dirname as up

cwd = os.getcwd()
wdir = up(cwd)
os.chdir(wdir)
import tools

path = r'W:\users\reinhardt\RESI\locmofit-avg\250122\RESI_cluster_center\analysis'

filename = 'avg_locs_all_13.hdf5'

blur_factor = 3

file = os.path.join(path,filename)

df = pd.read_hdf(file, key='locs')
print(df.keys())

print(df.head())
df['lpx'] = df['lpx']*blur_factor
df['lpy'] = df['lpy']*blur_factor

print(df.head())

new_filename = filename[:-5] + '_' + str(blur_factor) + "x_blurred.hdf5"
tools.picasso_hdf5(df, new_filename, filename, path + "/")


import os

from flask import Flask, render_template

from app.routes.home import home
from app.routes.api import api

def create_app(test_config=None):
    """Create and configure an instance of the Flask application."""
    app = Flask(__name__, instance_relative_config=True)
    app.config.from_mapping(
        # a default secret that should be overridden by instance config
        SECRET_KEY='dev'
    )

    if test_config is None:
        # load the instance config, if it exists, when not testing
        app.config.from_pyfile("config.py", silent=True)
    else:
        # load the test config if passed in
        app.config.update(test_config)

    # ensure the instance folder exists
    try:
        os.makedirs(app.instance_path)
    except OSError:
        pass

    @app.errorhandler(404)
    def not_found_page(error):
        return render_template('404.html', error_message=error), 404

    app.register_blueprint(api)
    app.register_blueprint(home)

    return app


import numpy as np

from classicML import _cml_precision
from classicML import CLASSICML_LOGGER
from classicML.api.models import BaseModel
from classicML.backend import get_kernel
from classicML.backend import get_optimizer
from classicML.backend import io

class SupportVectorClassifier(BaseModel):
    """支持向量分类器.

    Attributes:
        seed: int, default=None,
            随机种子.
        support: numpy.ndarray, default=None,
            分类器的支持向量下标数组.
        support_vector: numpy.ndarray, default=None,
            分类器的支持向量数组.
        support_alpha: numpy.ndarray, default=None,
            分类器的拉格朗日乘子数组.
        support_y: numpy.ndarray, default=None,
            分类器的支持向量对应的标签数组.
        b: float, default=0,
            偏置项.
        C: float, default=1.0,
            软间隔正则化系数.
        kernel: str, classicML.kernel.Kernels 实例, default='rbf'
            分类器使用的核函数.
        gamma: {'auto', 'scale'} or float, default='auto',
            在使用高斯核, sigmoid核或者多项式核时, 核函数系数,
            使用其他核函数时无效.
            - 如果gamma是'auto', gamma = 1 / 训练数据集的特征数.
            - 如果gamma是'scale', gamma = 1 / (训练数据集的特征数 * 训练数据集的方差).
        tol: float, default=1e-3,
            停止训练的最大误差值.
        epochs: int, default=-1,
            最大的训练轮数, 如果是-1则表示需要所有的样本满足条件时,
            才能停止训练, 即没有限制.
        optimizer: classicML.optimizers.Optimizer 实例
            SMO算法.
        is_trained: bool, default=False,
            模型训练后将被标记为True.
        is_loaded: bool, default=False,
            如果模型加载了权重将被标记为True.
    """
    def __init__(self, seed=None):
        """初始化分类器.

        Arguments:
            seed: int, default=None,
                随机种子.
        """
        super(SupportVectorClassifier, self).__init__()
        self.seed = np.random.seed(seed)

        self.support = None
        self.support_vector = None
        self.support_alpha = None
        self.support_y = None
        self.b = 0

        self.C = None
        self.kernel = None
        self.gamma = None
        self.tol = None
        self.epochs = None
        self.optimizer = get_optimizer('SMO')
        self.is_trained = False
        self.is_loaded = False

    def compile(self, C=1.0, kernel='rbf', gamma='auto', tol=1e-3):
        """编译分类器, 配置训练时使用的超参数.

        Arguments:
            C: float, default=1.0,
                软间隔正则化系数.
            kernel: str, classicML.kernel.Kernels 实例, default='rbf',
                分类器使用的核函数.
            gamma: {'auto', 'scale'} or float, default='auto',
                在使用高斯(径向基)核, sigmoid核或者多项式核时, 核函数系数,
                使用其他核函数时无效.
            tol: float, default=1e-3,
                停止训练的最大误差值.
        """
        self.C = C
        self.kernel = kernel
        self.gamma = gamma
        self.tol = tol

    def fit(self, x, y, epochs=1000, **kwargs):
        """训练分类器.

        Arguments:
            x: numpy.ndarray, array-like, 特征数据.
            y: numpy.ndarray, array-like, 标签.
            epochs: int, default=1000,
                最大的训练轮数, 如果是-1则表示需要所有的样本满足条件时,
                才能停止训练, 即没有限制.

        Returns:
            SupportVectorClassifier实例.
        """
        x = np.asarray(x, dtype=_cml_precision.float)
        y = np.asarray(y, dtype=_cml_precision.int)
        if y.ndim == 1:
            y = y.reshape(-1, 1)

        # 数值化gamma
        if self.gamma == 'auto':
            self.gamma = 1.0 / x.shape[1]
        elif self.gamma == 'scale':
            self.gamma = 1.0 / (x.shape[1] * x.var())

        # 获取核函数
        self.kernel = get_kernel(self.kernel, self.gamma)

        # 使用SMO算法优化
        (self.support, self.support_vector, self.support_alpha,
         self.support_y, self.b) = self.optimizer(x, y, self.C, self.kernel, self.tol, epochs)

        # 标记训练完成
        self.is_trained = True

        return self

    def predict(self, x, **kwargs):
        """使用分类器进行预测.

        Arguments:
            x: numpy.ndarray, array-like, 特征数据.

        Returns:
            SupportVectorClassifier预测的张量数组.

        Raises:
            ValueError: 模型没有训练的错误.
        """
        if self.is_trained is False and self.is_loaded is False:
            CLASSICML_LOGGER.error('模型没有训练')
            raise ValueError('你必须先进行训练')

        x = np.asarray(x, dtype=_cml_precision.float)
        number_of_sample = x.shape[0]
        y_pred = np.ones((number_of_sample, ), dtype=_cml_precision.int)

        for sample in range(number_of_sample):
            kappa = self.kernel(x[sample], self.support_vector)
            fx = np.matmul((self.support_alpha.reshape(-1, 1) * self.support_y).T, kappa.T) + self.b
            if fx < 0:
                y_pred[sample] = -1

        return y_pred

    def score(self, x, y):
        """在预测模式下计算准确率.

        Arguments:
            x: array-like, 特征数据.
            y: array-like, 标签.

        Returns:
            当前的准确率.
        """
        return super(SupportVectorClassifier, self).score(x, y)

    def load_weights(self, filepath):
        """加载模型参数.

        Arguments:
            filepath: str, 权重文件加载的路径.

        Raises:
            KeyError: 模型权重加载失败.

        Notes:
            模型将不会加载关于优化器的超参数.
        """
        # 初始化权重文件.
        parameters_gp = io.initialize_weights_file(filepath=filepath,
                                                   mode='r',
                                                   model_name='SupportVectorClassifier')
        # 加载模型参数.
        try:
            compile_ds = parameters_gp['compile']
            weights_ds = parameters_gp['weights']

            self.C = compile_ds.attrs['C']
            self.gamma = compile_ds.attrs['gamma']
            self.kernel = get_kernel(compile_ds.attrs['kernel'], self.gamma)
            self.tol = compile_ds.attrs['tol']

            self.support = weights_ds.attrs['support']
            self.support_vector = weights_ds.attrs['support_vector']
            self.support_alpha = weights_ds.attrs['support_alpha']
            self.support_y = weights_ds.attrs['support_y']
            self.b = weights_ds.attrs['b']
            # 标记加载完成
            self.is_loaded = True
        except KeyError:
            CLASSICML_LOGGER.error('模型权重加载失败, 请检查文件是否损坏')
            raise KeyError('模型权重加载失败')

    def save_weights(self, filepath):
        """将模型权重保存为一个HDF5文件.

        Arguments:
            filepath: str, 权重文件保存的路径.

        Raises:
            TypeError: 模型权重保存失败.

        Notes:
            模型将不会保存关于优化器的超参数.
        """
        # 初始化权重文件.
        parameters_gp = io.initialize_weights_file(filepath=filepath,
                                                   mode='w',
                                                   model_name='SupportVectorClassifier')
        # 保存模型参数.
        try:
            compile_ds = parameters_gp['compile']
            weights_ds = parameters_gp['weights']

            compile_ds.attrs['C'] = self.C
            compile_ds.attrs['kernel'] = self.kernel.name
            compile_ds.attrs['gamma'] = self.gamma
            compile_ds.attrs['tol'] = self.tol

            weights_ds.attrs['support'] = self.support
            weights_ds.attrs['support_vector'] = self.support_vector
            weights_ds.attrs['support_alpha'] = self.support_alpha
            weights_ds.attrs['support_y'] = np.asarray(self.support_y, dtype=np.float64)
            weights_ds.attrs['b'] = np.float64(self.b)
        except TypeError:
            CLASSICML_LOGGER.error('模型权重保存失败, 请检查文件是否损坏')
            raise TypeError('模型权重保存失败')


from commonfunctions import *
from pre_processing import *
from connected_componentes import *
from staff import calculate_thickness_spacing, remove_staff_lines, coordinator
from segmenter import Segmenter
from fit import predict
from glob import glob
import cv2
import pickle
from scipy.ndimage import binary_fill_holes
from skimage.morphology import thin
import argparse

label_map = {
    0: {
        0: 'N0'
    },
    1: {
        0: 'b2',
        1: 'a2'
    },
    2: {
        0: 'g2',
        1: 'f2'
    },
    3: {
        0: 'e2',
        1: 'd2'
    },
    4: {
        0: 'c2',
        1: 'b1'
    },
    5: {
        0: 'a1',
        1: 'g1'
    },
    6: {
        0: 'f1',
        1: 'e1'
    },
    7: {
        0: 'd1',
        1: 'c1'
    }
}

def estim(c, idx, imgs_spacing, imgs_rows):
    spacing = imgs_spacing[idx]
    rows = imgs_rows[idx]
    margin = 1+(spacing/4)
    for index, line in enumerate(rows):
        if c >= line - margin and c <= line + margin:
            return index+1, 0
        elif c >= line + margin and c <= line + 3*margin:
            return index+1, 1
    return 7, 1

def get_note_name(prev, octave, duration):
    if duration in ['4', 'a_4']:
        return f'{octave[0]}{prev}{octave[1]}/4'
    elif duration in ['8', '8_b_n', '8_b_r', 'a_8']:
        return f'{octave[0]}{prev}{octave[1]}/8'
    elif duration in ['16', '16_b_n', '16_b_r', 'a_16']:
        return f'{octave[0]}{prev}{octave[1]}/16'
    elif duration in ['32', '32_b_n', '32_b_r', 'a_32']:
        return f'{octave[0]}{prev}{octave[1]}/32'
    elif duration in ['2', 'a_2']:
        return f'{octave[0]}{prev}{octave[1]}/2'
    elif duration in ['1', 'a_1']:
        return f'{octave[0]}{prev}{octave[1]}/1'
    else:
        return "c1/4"

def filter_beams(prims, prim_with_staff, bounds):
    n_bounds = []
    n_prims = []
    n_prim_with_staff = []
    for i, prim in enumerate(prims):
        if prim.shape[1] >= 2*prim.shape[0]:
            continue
        else:
            n_bounds.append(bounds[i])
            n_prims.append(prims[i])
            n_prim_with_staff.append(prim_with_staff[i])
    return n_prims, n_prim_with_staff, n_bounds

def get_chord_notation(chord_list):
    chord_res = "{"
    for chord_note in chord_list:
        chord_res += (str(chord_note) + ",")
    chord_res = chord_res[:-1]
    chord_res += "}"

    return chord_res

def recognize(out_file, most_common, coord_imgs, imgs_with_staff, imgs_spacing, imgs_rows):
    black_names = ['4', '8', '8_b_n', '8_b_r', '16', '16_b_n', '16_b_r',
                   '32', '32_b_n', '32_b_r', 'a_4', 'a_8', 'a_16', 'a_32', 'chord']
    ring_names = ['2', 'a_2']
    whole_names = ['1', 'a_1']
    disk_size = most_common / 4
    if len(coord_imgs) > 1:
        out_file.write("{\n")
    for i, img in enumerate(coord_imgs):
        res = []
        prev = ''
        time_name = ''
        primitives, prim_with_staff, boundary = get_connected_components(
            img, imgs_with_staff[i])
        for j, prim in enumerate(primitives):
            prim = binary_opening(prim, square(
                np.abs(most_common-imgs_spacing[i])))
            saved_img = (255*(1 - prim)).astype(np.uint8)
            labels = predict(saved_img)
            octave = None
            label = labels[0]
            if label in black_names:
                test_img = np.copy(prim_with_staff[j])
                test_img = binary_dilation(test_img, disk(disk_size))
                comps, comp_w_staff, bounds = get_connected_components(
                    test_img, prim_with_staff[j])
                comps, comp_w_staff, bounds = filter_beams(
                    comps, comp_w_staff, bounds)
                bounds = [np.array(bound)+disk_size-2 for bound in bounds]

                if len(bounds) > 1 and label not in ['8_b_n', '8_b_r', '16_b_n', '16_b_r', '32_b_n', '32_b_r']:
                    l_res = []
                    bounds = sorted(bounds, key=lambda b: -b[2])
                    for k in range(len(bounds)):
                        idx, p = estim(
                            boundary[j][0]+bounds[k][2], i, imgs_spacing, imgs_rows)
                        l_res.append(f'{label_map[idx][p]}/4')
                        if k+1 < len(bounds) and (bounds[k][2]-bounds[k+1][2]) > 1.5*imgs_spacing[i]:
                            idx, p = estim(
                                boundary[j][0]+bounds[k][2]-imgs_spacing[i]/2, i, imgs_spacing, imgs_rows)
                            l_res.append(f'{label_map[idx][p]}/4')
                    res.append(sorted(l_res))
                else:
                    for bbox in bounds:
                        c = bbox[2]+boundary[j][0]
                        line_idx, p = estim(int(c), i, imgs_spacing, imgs_rows)
                        l = label_map[line_idx][p]
                        res.append(get_note_name(prev, l, label))
            elif label in ring_names:
                head_img = 1-binary_fill_holes(1-prim)
                head_img = binary_closing(head_img, disk(disk_size))
                comps, comp_w_staff, bounds = get_connected_components(
                    head_img, prim_with_staff[j])
                for bbox in bounds:
                    c = bbox[2]+boundary[j][0]
                    line_idx, p = estim(int(c), i, imgs_spacing, imgs_rows)
                    l = label_map[line_idx][p]
                    res.append(get_note_name(prev, l, label))
            elif label in whole_names:
                c = boundary[j][2]
                line_idx, p = estim(int(c), i, imgs_spacing, imgs_rows)
                l = label_map[line_idx][p]
                res.append(get_note_name(prev, l, label))
            elif label in ['bar', 'bar_b', 'clef', 'clef_b', 'natural', 'natural_b', 't24', 't24_b', 't44', 't44_b'] or label in []:
                continue
            elif label in ['#', '#_b']:
                if prim.shape[0] == prim.shape[1]:
                    prev = '##'
                else:
                    prev = '#'
            elif label in ['cross']:
                prev = '##'
            elif label in ['flat', 'flat_b']:
                if prim.shape[1] >= 0.5*prim.shape[0]:
                    prev = '&&'
                else:
                    prev = '&'
            elif label in ['dot', 'dot_b', 'p']:
                if len(res) == 0 or (len(res) > 0 and res[-1] in ['flat', 'flat_b', 'cross', '#', '#_b', 't24', 't24_b', 't44', 't44_b']):
                    continue
                res[-1] += '.'
            elif label in ['t2', 't4']:
                time_name += label[1]
            elif label == 'chord':
                img = thin(1-prim.copy(), max_iter=20)
                head_img = binary_closing(1-img, disk(disk_size))
            if label not in ['flat', 'flat_b', 'cross', '#', '#_b']:
                prev = ''
        if len(time_name) == 2:
            out_file.write("[ " + "\\" + "meter<\"" + str(time_name[0]) + "/" + str(time_name[1])+"\">" + ' '.join(
                [str(elem) if type(elem) != list else get_chord_notation(elem) for elem in res]) + "]\n")
        elif len(time_name) == 1:
            out_file.write("[ " + "\\" + "meter<\"" + '4' + "/" + '2' + "\">" + ' '.join(
                [str(elem) if type(elem) != list else get_chord_notation(elem) for elem in res]) + "]\n")
        else:
            out_file.write("[ " + ' '.join(
                [str(elem) if type(elem) != list else get_chord_notation(elem) for elem in res]) + "]\n")

    if len(coord_imgs) > 1:
        out_file.write("}")
    print("###########################", res, "##########################")

def main(input_path, output_path):
    imgs_path = sorted(glob(f'{input_path}/*'))
    for img_path in imgs_path:
        img_name = img_path.split('/')[-1].split('.')[0]
        out_file = open(f'{output_path}/{img_name}.txt', "w")
        print(f"Processing new image {img_name}...")
        img = io.imread(img_path)
        img = gray_img(img)
        horizontal = IsHorizontal(img)
        if horizontal == False:
            theta = deskew(img)
            img = rotation(img, theta)
            img = get_gray(img)
            img = get_thresholded(img, threshold_otsu(img))
            img = get_closer(img)
            horizontal = IsHorizontal(img)

        original = img.copy()
        gray = get_gray(img)
        bin_img = get_thresholded(gray, threshold_otsu(gray))

        segmenter = Segmenter(bin_img)
        imgs_with_staff = segmenter.regions_with_staff
        most_common = segmenter.most_common

        # imgs_without_staff = segmenter.regions_without_staff

        imgs_spacing = []
        imgs_rows = []
        coord_imgs = []
        for i, img in enumerate(imgs_with_staff):
            spacing, rows, no_staff_img = coordinator(img, horizontal)
            imgs_rows.append(rows)
            imgs_spacing.append(spacing)
            coord_imgs.append(no_staff_img)

        print("Recognize...")
        recognize(out_file, most_common, coord_imgs,
                  imgs_with_staff, imgs_spacing, imgs_rows)
        out_file.close()
        print("Done...")

if __name__ == "__main__":
    parser = argparse.ArgumentParser()

    parser.add_argument("inputfolder", help="Input File")
    parser.add_argument("outputfolder", help="Output File")

    args = parser.parse_args()
    main(args.inputfolder, args.outputfolder)


from scipy.io import wavfile
from scipy import signal
import dspalgorithm as dsp
import numpy  as np
import matplotlib.pyplot as plt
fs,x=wavfile.read('for1.wav')
x=np.double(x)
fp=1500
fs=3000
ap=3
as1=10
F=8000
N,wc=dsp.buttord(fp,fs,ap,as1,F)
print(N)
print(wc)
b,a=signal.butter(N,wc)
print(b)
print(a)
y=signal.lfilter(b,a,x)
plt.subplot(2,1,1)
plt.plot(x)
plt.subplot(2,1,2)
plt.plot(y)
plt.show()
y=np.int16(y)
wavfile.write(example, 20,y)


import os
import slack
import logging
from enum import Enum

logger = logging.getLogger(__name__)

def verify_priorities(channel_id, priorities, user_id):
    """
    Changes the priority of a Google Cloud Support case.

    Parameters
    ----------
    channel_id : str
      unique string used to idenify a Slack channel. Used to send messages to
      the channel
    priorities : str[]
      the current priority of the case, represented as P1, P2, P3, or P4
    user_id : str
      the Slack user_id of the user who submitted the request. Used to send
      ephemeral messages to the user

    Returns
    ----------
    bool
        Validates whether or not all of the provided priorities are in
        our enum
    """
    valid_priorities = Enum('Priority', ['P1', 'P2', 'P3', 'P4'])
    client = slack.WebClient(token=os.environ.get("SLACK_TOKEN"))
    for priority in priorities:
        if priority not in valid_priorities.__members__:
            client.chat_postEphemeral(
                channel=channel_id,
                user=user_id,
                text=(f"Your priority `{priority}` was not valid. Please"
                      " provide a priority of value `P1` | `P2` | `P3`| `P4`")
                )
            return False
    return True

if __name__ == "__main__":
    test_channel_id = os.environ.get("TEST_CHANNEL_ID")
    test_priority = ['P3']
    test_user_id = os.environ.get("TEST_USER_ID")
    verify_priorities(test_channel_id, test_priority,
                      test_user_id)


"""
https://leetcode.com/problems/perfect-squares/

Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, ...) which sum to n.
"""

class Solution:

    def numSquares(self, n: int) -> int:
        arr = [n] * (n + 1)
        if n <= 3:
            return n

        for _ in range(4):
            arr[_] = _

        for i in range(4, n + 1):
            for j in range(int(i ** 0.5), 0, -1):
                arr[i] = min(arr[i], 1 + arr[i - j * j])
                if arr[i] <= 2:
                    break

        return arr[-1]

if __name__ == '__main__':
    n = 12
    res = Solution().numSquares(n)
    print(res)


import torch
import numpy as np
import cv2
import os
import shutil

images_path = '/home/srq/Datasets/tables/uw3/correctgt-zones/'
output_path = '/home/srq/Datasets/tables/uw3-for-auto-encoders'

for i in os.listdir(images_path):
    if not i.endswith('.png'):
        continue
    id = os.path.splitext(i)[0]
    print(id)
    output_create_dir = os.path.join(output_path,id)
    if not os.path.exists(output_create_dir):
        os.mkdir(output_create_dir)
    image_out = os.path.join(output_create_dir, 'image.png')
    shutil.copy(os.path.join(images_path, i), image_out)


import numpy as np
import cv2
import math
from scipy import ndimage

img_before = cv2.imread('C:/Users/Dero/Desktop/baidu_captcha/vcode-spin-img108.png')

cv2.imshow("Before", img_before)
key = cv2.waitKey(0)

img_gray = cv2.cvtColor(img_before, cv2.COLOR_BGR2GRAY)
cv2.imshow("gray", img_gray)
key = cv2.waitKey(0)
img_edges = cv2.Canny(img_gray, 100, 100, apertureSize=3)
# 边缘检测
lines = cv2.HoughLinesP(img_edges, 1, math.pi / 180.0, 80, minLineLength=100, maxLineGap=5)
# 检测标准霍夫变化的二值图像直线线条

angles = []

for [[x1, y1, x2, y2]] in lines:
    cv2.line(img_before, (x1, y1), (x2, y2), (255, 0, 0), 3)
    angle = math.degrees(math.atan2(y2 - y1, x2 - x1))
    angles.append(angle)

cv2.imshow("Detected lines", img_before)
key = cv2.waitKey(0)

median_angle = np.median(angles)
img_rotated = ndimage.rotate(img_before, median_angle)

print(f"Angle is {median_angle:.04f}")
rotation_angel = round(median_angle, 2)
print(rotation_angel)
cv2.imwrite('rotated.jpg', img_rotated)

# 结果为负数，表示需要顺时针旋转角度，正为逆时针旋转
#


#For Google Colab Version
#https://colab.research.google.com/drive/1oz8shKHjbXwDdqui_GtvQKvmT3vesTZW?usp=share_link

from keras.models import Sequential
from keras.layers import Input, Dense
from keras.utils import to_categorical
from sklearn.metrics import confusion_matrix
import numpy as np

#Create model by using sequential structure
model = Sequential()
model.add(Dense(10, input_dim=5, activation='tanh'))
model.add(Dense(10, activation='tanh'))
model.add(Dense(3, activation='softmax'))

model.compile(optimizer='adam',
              loss='categorical_crossentropy',
              metrics=['accuracy'])

model.summary()

#Read data from file (download at https://github.com/neokarn/computer_vision/blob/master/data.csv)
data = np.asarray([[float(num) for num in line.split(',')] for line in open('data.csv')])

#Train Model
x_train = data[0:120,0:5]
y_train = data[0:120,5]
y_train = to_categorical(y_train)

model.fit(x_train, y_train, epochs=100, batch_size=5)

#Test Model
x_test = data[120:,0:5]
y_test = data[120:,5]

y_pred = model.predict(x_test)
y_pred = np.argmax(y_pred,axis = -1)
cm = confusion_matrix(y_test, y_pred)
print("Confusion Matrix")
print(cm)


from pickle import decode_long
import sys

def bs(left, right, i):
    while(left <= right):
        mid = (left+right)//2
        if lis[mid] < elec[i]:
            left = mid + 1
        else:
            right = mid-1
    return left

if __name__ == "__main__":
    N = int(sys.stdin.readline())
    elec = list(map(int, sys.stdin.readline().split()))
    cnt = 1
    lis = list( 0 for _ in range(N))
    lis[0] = elec[0]

    for i in range(N):
        if lis[cnt-1] < elec[i]:
            lis[cnt] = elec[i]
            cnt += 1
        else:
            left = 0
            right = cnt
            ind = bs(left, right, i)
            lis[ind] = elec[i]

    print(N-cnt)

백준/Gold/1365. 꼬인 전깃줄/꼬인 전깃줄.py


import requests
import random

import config

class Hangman():

	def __init__(self):
	    self.used_letters = []
	    self.lives = 5
	    self.hidden_word = []

	def import_words(self):
		#word_site = config.word_site
		#response = requests.get(word_site)
		#WORDS = response.content.splitlines()
		#return WORDS
		return ['parmesan', 'poker', 'baguette', 'bottle', 'table', 'keyboard', 'internship', 'friend']

	def get_word(self, word_list):
		word_to_guess = random.choice(word_list)
		return word_to_guess

	def get_guess(self):
		letter_guessed = input('What is your guess : ')
		if letter_guessed in self.used_letters:
			print('Letter already used')
			letter_to_return = self.get_guess()
			return letter_to_return
		elif letter_guessed.isalpha() and len(letter_guessed) == 1:
			self.used_letters.append(letter_guessed)
			return letter_guessed
		else:
			print('Please enter a letter.')
			letter_to_return = self.get_guess()
			return letter_to_return

	def compare(self, letter_guessed, word_to_guess):
		if letter_guessed in word_to_guess:
			for index, letter in enumerate(word_to_guess):
				if letter == letter_guessed:
					self.hidden_word[index] = letter
			print('You were right !')
			print('word : ', self.hidden_word)
			print('lives : ', self.lives)
			print('Used letters : ', self.used_letters)

		else:
			print('Wrong, you lost a life !')
			self.lives -= 1
			print('word : ', self.hidden_word)
			print('lives : ', self.lives)
			print('Used letters : ', self.used_letters)

	def game(self):
		word_list = self.import_words()
		word_to_guess = self.get_word(word_list)
		for i in range(len(word_to_guess)):
			self.hidden_word.append('*')
		while self.lives != 0:
			print('----------------------------------')
			letter_guessed = self.get_guess()
			self.compare(letter_guessed, word_to_guess)
			if ''.join(self.hidden_word) == word_to_guess:
				print('You won !')
				break
			if self.lives == 0:
				print('You lost !')

g = Hangman()
g.game()


from sympy import Sum

n = int(input())
arr = sorted(list(map(int, input().split())))

start = 0
end = len(arr)-1

min = 1e9
while start < end:
    Sum = arr[start] + arr[end]  # -2 4 -99 -1 98 -> -99 -2 -1 4 98
    if abs(Sum) < min:
        min = abs(Sum)
        answer = arr[start], arr[end]
    if Sum == 0:
        break
    if Sum < 0:
        start += 1
    else:
        end -= 1

for i in sorted(answer):
    print(i, end=' ')


import logging
import requests
from nakhll.settings import KAVENEGAR_KEY
from sms.models import SMS, SMSRequest
logger = logging.getLogger(__name__)

# only for test purposes
def count_sms():
    return SMS.objects.all().count()

def create_sms(**kwargs):
    SMS.objects.create(**kwargs)

def send_sms(Title, Description, PhoneNumber):
    # Change Text
    des_list = Description.split(' ')
    final_des = ''
    count = 0
    for item in des_list:
        if (count + 1) == len(des_list):
            final_des += item
            count += 1
        else:
            final_des += item + '-'
            count += 1

    url = 'https://api.kavenegar.com/v1/{}/verify/lookup.json'.format(KAVENEGAR_KEY)
    params = {'receptor': PhoneNumber, 'token': Title, 'token2': final_des, 'template': 'nakhll-alert'}
    requests.post(url, params=params)

class Kavenegar:
    """Interface for interacting with Kavenegar SMS API

    There is a function for each need in our project to send SMS. Also there is a private low level function to send
    direct SMS using Kavenegar API.
    """
    @staticmethod
    def _raw_send(template, receptor, tokens):
        """Low level function to send SMS using Kavenegar API.

        Args:
            template: The template name of the SMS which should be sumbitted and verified in Kavenegar website.
                These templates contains up to 3 tokens which will replace the tokens in the template.
            receptor: The phone number of the receptor of the SMS.
            tokens: The dictionary of tokens which should be replaced in the template. For example:
                {'token': '12345', 'token2': '67890'}
        """
        url = 'https://api.kavenegar.com/v1/{}/verify/lookup.json'.format(KAVENEGAR_KEY)
        params = {'receptor': receptor, 'template': template, **tokens}
        requests.post(url, params=params)

    @staticmethod
    def alert(receptor, title, description):
        """Not using anymore."""
        tokens = {
            'token': title,
            'token2': description,
        }
        Kavenegar._raw_send('nakhll-alert', receptor, tokens)

    @staticmethod
    def shop_new_order(receptor, order_id):
        """Send order_id to shop owner who has new order."""
        logger.debug('Sending new order SMS to {} for order {}'.format(receptor, order_id))
        tokens = {'token': order_id}
        Kavenegar._raw_send('nakhll-order', receptor, tokens)

    @staticmethod
    def send_auth_code(mobile_number, code):
        """Send auth code to user who wants to perform any auth request."""
        logger.debug('Sending auth code {} to {}'.format(code, mobile_number))
        tokens = {'token': code}
        Kavenegar._raw_send('nakhl-register', mobile_number, tokens)

    @staticmethod
    def invoice_has_been_sent(invoice):
        """Send invoice number and shipping tracking codes to buyer when invoice has been sent."""
        tokens = {
            'token': invoice.id,
            'token2': '-'.join(invoice.barcodes),
        }
        template = 'nakhll-sendpostcode'
        mobile = invoice.user.username
        Kavenegar._raw_send(template, mobile, tokens)

    @staticmethod
    def send_gift_coupon(user, coupon):
        tokens = {
            'token': coupon.code
        }
        mobile = user.username
        template = 'nakhl-sendgiftcoupon'
        Kavenegar._raw_send(template, mobile, tokens)


from pyVmomi import vim

from vmwarelib import inventory
from vmwarelib import checkinputs
from vmwarelib.actions import BaseAction

class VMCreateBareBones(BaseAction):
    def run(self, vm_name, cluster, datastore_cluster,
            datastore, resourcepool, cpu_size, ram_size,
            guestos, version, description, vsphere=None):
        """
        Create barebones VM (CPU/RAM/Graphics)

        Args:
        - vm_name: Name of Virtual Machine to create
        - vsphere: Pre-configured vsphere connection details (config.yaml)
        - description: Short Description of VM and it's purpose
        - cpu_size: Number of vCPUs to allocate
        - ram_size: Ammount of memory to assign (GB)
        - datastore_cluster: name of DataStore Cluster to use for VM Files
        - datastore: Individual datastore to put vm files within.
                     Not needed if datastore_cluster is set
        - cluster: Cluster within vsphere to host virtual machine
        - version: VM version to set
        - guestos: Code for GuestOS that will be installed on this VM
        - resourepool: vsphere resource pool to assign new VM to

        Returns:
        - dict: vm moid of newly created vm
        """
        # Setup Identifiers for objects
        self.establish_connection(vsphere)
        si = self.si
        si_content = si.RetrieveContent()
        # checkinputs.vm_storage(datastore_cluster, datastore)
        checkinputs.one_of_two_strings(datastore_cluster,
                                       datastore,
                                       "Datastore Cluster or Datastore")

        data_center = self.si_content.rootFolder.childEntity[0]
        cluster = inventory.get_cluster(self.si_content, name=cluster)
        data_store_cluster = inventory.get_datastore_cluster(
            self.si_content,
            name=datastore_cluster)
        # data_store = inventory.get_datastore(self.si_content, name=datastore)
        target_folder = data_center.vmFolder

        # If No Resource Pool issued the Default one for
        # the Cluster will be selected.
        if resourcepool:
            resource_pool = inventory.get_resource_pool(self.si_content,
                                                        name=resourcepool)
        else:
            resource_pool = cluster.resourcePool

        # Config created that is required for DS selection
        # Config is BareBones, CPU RAM no more.
        config = vim.vm.ConfigSpec(name=vm_name,
                                   memoryMB=(ram_size * 1024),
                                   numCPUs=cpu_size,
                                   guestId=guestos,
                                   version=version,
                                   cpuHotAddEnabled=True,
                                   memoryHotAddEnabled=True,
                                   annotation=description)

        # if Datastore cluster is provided it will find the
        # recommended Datastore to store VM files
        if datastore_cluster:
            podsel = vim.storageDrs.PodSelectionSpec()
            podsel.storagePod = data_store_cluster

            storage_spec = vim.storageDrs.StoragePlacementSpec(
                type='create',
                configSpec=config,
                resourcePool=resource_pool,
                podSelectionSpec=podsel,
                folder=target_folder)

            # Create Instance of Storage Resource Manager -
            # This is used to identify a Recommended Datastore from a Cluster
            srm = si_content.storageResourceManager
            results = srm.RecommendDatastores(storageSpec=storage_spec)
            rec_ds = results.recommendations[0].action[0]\
                .relocateSpec.datastore
            datastore_path = '[' + rec_ds.name + '] ' + vm_name
        else:
            # No Datastore Cluster has been offered so using the D
            if datastore:
                datastore_path = '[' + datastore + '] ' + vm_name
            else:
                raise Exception('Error No Storage Data Provided')

        # Now Datastore is known the remaining
        # of VM Config can be setup and added
        vmx_file = vim.vm.FileInfo(logDirectory=None,
                                   snapshotDirectory=None,
                                   suspendDirectory=None,
                                   vmPathName=datastore_path)
        config.files = vmx_file

        # Create task to Build actual Machine
        task = target_folder.CreateVM_Task(config=config, pool=resource_pool)
        self._wait_for_task(task)
        if task.info.state != vim.TaskInfo.State.success:
            raise Exception(task.info.error.msg)

        return {'vm_id': task.info.result._moId}


import warnings
warnings.filterwarnings('ignore')

import sys
sys.path.insert(0, "../../")

import argparse
import pandas as pd
from numpy import save
import load_data
import joblib
from sklearn.pipeline import FeatureUnion, Pipeline
import build_library.utils

class Data:
    """
    Loads data, samples training data if specified, assigns features_df and target_df
    """
    full_pipeline = None
    clf_param = {}

    def __init__(self, file, target, sample=False, n_samples=None, frac=None):
        self.file = file
        self. sample = sample
        self.n_samples = n_samples
        self.target = target
        self.frac = frac

    def get_data(file, target, sample=False, n_samples=None, frac=None):
        """Load train data with option to sample
        Args:
            frac= fraction(percent) of sample data to load for training
        """
        data = pd.read_csv(file)
        if sample:
            """Sample train data due to resource limitation"""
            data = data.groupby(target).apply(lambda x: x.sample(frac=frac))
            data = data.reset_index(drop=True)
        else:
            data = data
        print(data.shape, 'data loaded\n')
        return data

    def get_features(data):
        """Assign features dataframe"""
        features_df = data
        print(features_df.shape, 'features assigned\n')
        return features_df

    def get_target(data, target):
        """Assign target"""
        target_df = data[target].values
        print(target_df.shape, '...target rows loaded\n')
        return target_df

def transform_data(numpy_file_name, transformed_data):
    """
    Saves transformed numpy array as a .npy file
    """
    save(numpy_file_name, transformed_data)

def transform_and_saved_data(config_path, test=False):
    """
    input: data csv
    output: transformed data for modeling (a numpy array)
    """
    config = load_data.read_params(config_path)
    sample = config["transform_data_config"]["sample"]
    frac = config["transform_data_config"]["frac"]
    target = config["raw_data_config"]["target"]
    seed = config["raw_data_config"]["random_state"]
    categorical_features = config["transform_data_config"]["categorical_features"]
    numerical_features = config["transform_data_config"]["numerical_features"]
    transformed_data = config["transform_data_config"]["transformed_data"]
    transformed_target = config["transform_data_config"]["transformed_target"]
    transformed_test_data = config["transform_data_config"]["transformed_test_data"]
    transformed_test_target = config["transform_data_config"]["transformed_test_target"]
    transform_pipeline = config["transform_data_config"]["transform_pipeline"]

    if test:
        """transforms test data"""
        file = config["processed_data_config"]["test_data_csv"]
        transformed_data = transformed_test_data
        transformed_target = transformed_test_target
    else:
        """transforms train data"""
        file = config["processed_data_config"]["train_data_csv"]
        transformed_data = transformed_data
        transformed_target = transformed_target

    """load data"""
    data = Data.get_data(file, target=target, sample=sample, frac=frac)

    """get features"""
    features_df = Data.get_features(data)

    """get target"""
    target_df = Data.get_target(data, target=target)

    """Data transformation pipelines"""
    """define steps in the categorical pipeline"""
    categorical_pipeline = Pipeline(steps=[('cat_selector', build_library.utils.FeatureSelector(categorical_features)),
                                           ('cat_feats_add', build_library.utils.CategoricalFeatsAdded()),
                                           ('delete_unused', build_library.utils.DelUnusedCols(features_df))])
    """define the steps in the numerical pipeline"""
    numerical_pipeline = Pipeline(steps=[('num_selector', build_library.utils.FeatureSelector(numerical_features)),
                                         ('remove_negative_values', build_library.utils.RemoveNegativeValues(features_df)),
                                         ('impute_missing', build_library.utils.SimpleImputerTransformer(features_df)),
                                         ('cap_outliers', build_library.utils.CapOutliers(features_df))])

    """combine numerical and categorical pipeline into one full big pipeline horizontally using FeatureUnion"""
    Data.full_pipeline = FeatureUnion(transformer_list=[('categorical_pipeline', categorical_pipeline),
                                                        ('numerical_pipeline', numerical_pipeline)])

    if not test:
        """save transformation pipeline"""
        Data.clf_param["preprocessing"] = Data.full_pipeline
        # serialize transformation pipeline object to disk
        joblib.dump(Data.clf_param, transform_pipeline)
        print("Saved preprocessing transform_pipeline to disk")

    """disable pandas chained_assignment warning"""
    pd.options.mode.chained_assignment = None

    """fit data to data transformation pipeline"""
    prepared_features = Data.full_pipeline.fit_transform(features_df)
    transform_data(transformed_data, prepared_features)
    transform_data(transformed_target, target_df)

if __name__ == "__main__":
    args = argparse.ArgumentParser()
    args.add_argument("--config", default="params.yaml")
    parsed_args = args.parse_args()
    print()
    print("******** Transformed Train Data *************")
    transform_and_saved_data(config_path=parsed_args.config)
    print()
    print("******** Transformed Test Data *************")
    transform_and_saved_data(config_path=parsed_args.config, test=True)