# __________________________ postproceso_1d.py _________________________ #

# :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: #
# :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: #

# variables globales
axisNum=0
file_names=[]
# permite diferenciar
markers = [',', '+', '.', 'o', '*']
linestyles = ['_', '-', '--', ':']
colors = ['b', 'g', 'r', 'c', 'm', 'y', 'k']

# :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: #
# :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: #

def CONVERGENCIA_NPT(filenames,**kwargs):

    #__________ funciones locales (graficos) __________

    def preambulo_conv_fwls_npt():

        # formato fuente (permite utilizar latex, e.g. r'\omega')
        plt.rc('text', usetex=True)
        plt.rc('font', family='serif')

        # titulo
        fig.suptitle(r'An\'alisis de convergencia 1D, función de forma FWLS, num. p/ nube variable')

        # subplot 1, funcion u
        #ax1.set_title(r'Error norma $L2$')
        ax1.set_xlabel(r'N\'umero de puntos [-]')
        ax1.set_ylabel(r'Error norma $L2$, $\epsilon_{L2}$')
        #ax1.set_xscale('log')
        #ax1.set_yscale('log')

        # subplot 2, derivada de u
        #ax2.set_title(r'Error norma $H2$')
        ax2.set_xlabel(r'N\'umero de puntos [-]')
        ax2.set_ylabel(r'Error norma $H2$, $\epsilon_{H2}$')
        #ax2.set_xscale('log')
        #ax2.set_yscale('log')

        # subplot 3, funcion u
        #ax3.set_title(r'Error norma $L2$')
        ax3.set_xlabel(r'$h_{car}$')
        ax3.set_ylabel(r'Error norma $L2$, $\epsilon_{L2}$')
        ax3.set_xscale('log')
        #ax3.set_yscale('log')

        # subplot 4, derivada de u
        #ax4.set_title(r'Error norma $H2$')
        ax4.set_xlabel(r'$h_{car}$')
        ax4.set_ylabel(r'Error norma $H2$, $\epsilon_{H2}$')
        ax4.set_xscale('log')
        #ax4.set_yscale('log')

        return [ax1,ax2,ax3,ax4,fig]

    def graficar_conv_fwls_npt():

        global axisNum,markers,linestyles,colors

        # seleccionar
        marker    = markers[ axisNum % len(markers) ]
        linestyle = linestyles[ axisNum % len(linestyles) ]
        color     = colors[ axisNum % len(colors) ]
        axisNum += 1

        # agrupa la info
        x_=[] ; y1_=[] ; y2_=[] ; files_=[]
        for sol in soluciones:
            x_.append(sol.nnods)
            y1_.append(sol.L2_norm_error)
            y2_.append(sol.H2_norm_error)
            files_.append(sol.filename) #contiene info

        # ordena la info
        from numpy import argsort
        sort_list=argsort(x_)
        x=[] ; y1=[] ; y2=[] ; files=[]
        for index in sort_list:
            x.append(x_[index])
            y1.append(y1_[index])
            y2.append(y2_[index])
            files.append(files_[index])

        # calculo preliminar (axis 3 y 4)
        x2=[]
        L = max(soluciones[0].coord) - min(soluciones[0].coord)
        for n in x:
            x2.append(L/(n-1)) # se calcula como promedio
                                                 # (puede ser otro criterio)
        h = max(x)-min(x)
        if ( min(x2) < 1.0 ):
            for i in range(len(x2)):
                x2[i] += 1.0-min(x2)+h*0.01

        # subplot 1, funcion u
        #ax1.scatter(x,y1,color=color,marker=marker)
        ax1.plot(x,y1,color=color,label=npt)
        ax1.set_xlim(min(x)-h*0.05,max(x)+h*0.1)

        # subplot 2, derivada de u
        #ax2.scatter(x,y2,color=color,marker=marker)
        ax2.plot(x,y2,color=color,label=npt)
        ax2.set_xlim(min(x)-h*0.05,max(x)+h*0.1)
        h = max(x2)-min(x2)

        # considera los 'nlim'-primeros valores (acota grafico con x-log)
        nlim = 0 # 0 muestra todos los resultados
        h = max(x2[nlim:-1])-min(x2[nlim:-1])

        # subplot 3, funcion u
        #ax3.scatter(x2[nlim:-1],y1[nlim:-1],color=color,marker=marker)
        ax3.plot(x2[nlim:-1],y1[nlim:-1],color=color,label=npt)
        ax3.set_xlim(min(x2[nlim:-1])-h*0.1,max(x2[nlim:-1])+h*0.1)

        # subplot 4, derivada de u
        #ax4.scatter(x2[nlim:-1],y2[nlim:-1],color=color,marker=marker)
        ax4.plot(x2[nlim:-1],y2[nlim:-1],color=color,label=npt)
        ax4.set_xlim(min(x2[nlim:-1])-h*0.1,max(x2[nlim:-1])+h*0.1)

        return

    def exportar_conv_fwls_npt():

        # grafica textos (labels) de cada eje
        ax1.legend();ax2.legend();ax3.legend();ax4.legend()

        # ajuste automatico del tamaño de cada subfigura
        fig.tight_layout()

        #.... crea directorio si es que no existe ....#
        data_dir = "../GRAFICOS/test1d/convergencia/"
        import os
        if ( not os.path.isdir(data_dir) ):
            os.mkdir(data_dir)

        # expotar / mostrar resultados
        rango='npt_'+npts[0]+'_a_'+npts[-1]
        file_ = files
        file_ = file_.split('/')[-1].split('_')[:3]
        file_ = '_'.join(file_)+'_'+rango+'.svg'

        plt.savefig(data_dir+file_)

        show = kwargs.get('show',False)
        if (show):
            plt.show()

        plt.cla()   # clear axis
        plt.clf()   # clear figure
        plt.close() # close a figure windows

        return

    # __________ funcion principal __________

    # contar numero de parametros dmax ingresados
    filenames = filenames.split(',')
    npts = []
    for files in filenames:
        npt = files.split('/')[-1].split('_')[-1] # extrae "nptval.dat"
        npt = npt[:len(npt)-4]                # recorta ".dat"
        if ( not npt in npts ):
            npts.append(npt)
    npts = sorted(npts)            # ordena de menor a mayor

    # separar filenames segun funcion de forma
    files_dict={}
    for npt in npts:
        files_dict[npt] = []
    for files in filenames:
        npt = files.split('/')[-1].split('_')[-1] # extrae "dmaxval.dat"
        npt = npt[:len(npt)-4]                # recorta ".dat"
        files_dict[npt].append(files)

    # genera objetos matplotlib (fig y axis)
    import matplotlib.pyplot as plt
    fig=plt.figure(figsize=(10,5))
    ax1 = fig.add_subplot(2,2,1)
    ax2 = fig.add_subplot(2,2,2)
    ax3 = fig.add_subplot(2,2,3)
    ax4 = fig.add_subplot(2,2,4)
    # preambulo (init settings)
    preambulo_conv_fwls_npt()

    # loop sobre cada parametro dmax
    for npt in files_dict:
        soluciones = []

        # loop sobre cada archivo asociado a un dmax (coeff)
        for files in files_dict[npt]:

            # Procesamiento y almacenamiento de data
            soluciones.append(Meshfree_Collocation_Postprocess(files))

        # grafica solucion
        graficar_conv_fwls_npt()

    exportar_conv_fwls_npt()

    return

# :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: #
# :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: #

def CONVERGENCIA_DMAX(filenames,**kwargs):

    #__________ funciones locales (graficos) __________

    def preambulo_conv_lme_dmax():

        # formato fuente (permite utilizar latex, e.g. r'\omega')
        plt.rc('text', usetex=True)
        plt.rc('font', family='serif')

        # titulo
        fig.suptitle(r'An\'alisis de convergencia 1D, función de forma LME, $d_{max}$ variable')

        # subplot 1, funcion u
        #ax1.set_title(r'Error norma $L2$')
        ax1.set_xlabel(r'N\'umero de puntos [-]')
        ax1.set_ylabel(r'Error norma $L2$, $\epsilon_{L2}$')
        #ax1.set_xscale('log')
        #ax1.set_yscale('log')

        # subplot 2, derivada de u
        #ax2.set_title(r'Error norma $H2$')
        ax2.set_xlabel(r'N\'umero de puntos [-]')
        ax2.set_ylabel(r'Error norma $H2$, $\epsilon_{H2}$')
        #ax2.set_xscale('log')
        #ax2.set_yscale('log')

        # subplot 3, funcion u
        #ax3.set_title(r'Error norma $L2$')
        ax3.set_xlabel(r'$h_{car}$')
        ax3.set_ylabel(r'Error norma $L2$, $\epsilon_{L2}$')
        ax3.set_xscale('log')
        #ax3.set_yscale('log')

        # subplot 4, derivada de u
        #ax4.set_title(r'Error norma $H2$')
        ax4.set_xlabel(r'$h_{car}$')
        ax4.set_ylabel(r'Error norma $H2$, $\epsilon_{H2}$')
        ax4.set_xscale('log')
        #ax4.set_yscale('log')

        return [ax1,ax2,ax3,ax4,fig]

    def graficar_conv_lme_dmax():

        global axisNum,markers,linestyles,colors

        # seleccionar
        marker    = markers[ axisNum % len(markers) ]
        linestyle = linestyles[ axisNum % len(linestyles) ]
        color     = colors[ axisNum % len(colors) ]
        axisNum += 1

        # agrupa la info
        x_=[] ; y1_=[] ; y2_=[] ; files_=[]
        for sol in soluciones:
            x_.append(sol.nnods)
            y1_.append(sol.L2_norm_error)
            y2_.append(sol.H2_norm_error)
            files_.append(sol.filename) #contiene info

        # ordena la info
        from numpy import argsort
        sort_list=argsort(x_)
        x=[] ; y1=[] ; y2=[] ; files=[]
        for index in sort_list:
            x.append(x_[index])
            y1.append(y1_[index])
            y2.append(y2_[index])
            files.append(files_[index])

        # calculo preliminar (axis 3 y 4)
        x2=[]
        L = max(soluciones[0].coord) - min(soluciones[0].coord)
        for n in x:
            x2.append(L/(n-1)) # se calcula como promedio
                                                 # (puede ser otro criterio)
        h = max(x)-min(x)
        if ( min(x2) < 1.0 ):
            for i in range(len(x2)):
                x2[i] += 1.0-min(x2)+h*0.01

        # subplot 1, funcion u
        #ax1.scatter(x,y1,color=color,marker=marker)
        ax1.plot(x,y1,color=color,label=coeff)
        ax1.set_xlim(min(x)-h*0.05,max(x)+h*0.1)

        # subplot 2, derivada de u
        #ax2.scatter(x,y2,color=color,marker=marker)
        ax2.plot(x,y2,color=color,label=coeff)
        ax2.set_xlim(min(x)-h*0.05,max(x)+h*0.1)
        h = max(x2)-min(x2)

        # subplot 3, funcion u
        #ax3.scatter(x2,y1,color=color,marker=marker)
        ax3.plot(x2,y1,color=color,label=coeff)
        ax3.set_xlim(min(x2)-h*0.1,max(x2)+h*0.1)

        # subplot 4, derivada de u
        #ax4.scatter(x2,y2,color=color,marker=marker)
        ax4.plot(x2,y2,color=color,label=coeff)
        ax4.set_xlim(min(x2)-h*0.1,max(x2)+h*0.1)

        return

    def exportar_conv_lme_dmax():

        # grafica textos (labels) de cada eje
        ax1.legend();ax2.legend();ax3.legend();ax4.legend()

        # ajuste automatico del tamaño de cada subfigura
        fig.tight_layout()

        #.... crea directorio si es que no existe ....#
        data_dir = "../GRAFICOS/test1d/convergencia/"
        import os
        if ( not os.path.isdir(data_dir) ):
            os.mkdir(data_dir)

        # expotar / mostrar resultados
        rango='dmax_'+dmax[0]+'_a_'+dmax[-1]
        file_ = files.split('/')[-1].split('_')
        del file_[1] ; file_=file_[:-2]
        file_ = '_'.join(file_)+'_'+rango+'.svg'

        plt.savefig(data_dir+file_)

        show = kwargs.get('show',False)
        if (show):
            plt.show()

        plt.cla()   # clear axis
        plt.clf()   # clear figure
        plt.close() # close a figure windows

        return

    # __________ funcion principal __________

    # contar numero de parametros dmax ingresados
    filenames = filenames.split(',')
    dmax = []
    for files in filenames:
        coeff = files.split('/')[-1].split('_')[-1] # extrae "dmaxval.dat"
        coeff = coeff[:len(coeff)-4]                # recorta ".dat"
        if ( not coeff in dmax ):
            dmax.append(coeff)
    dmax = sorted(dmax)            # ordena de menor a mayor

    # separar filenames segun funcion de forma
    files_dict={}
    for coeff in dmax:
        files_dict[coeff] = []
    for files in filenames:
        coeff = files.split('/')[-1].split('_')[-1] # extrae "dmaxval.dat"
        coeff = coeff[:len(coeff)-4]                # recorta ".dat"
        files_dict[coeff].append(files)

    # genera objetos matplotlib (fig y axis)
    import matplotlib.pyplot as plt
    fig=plt.figure(figsize=(10,6))
    ax1 = fig.add_subplot(2,2,1)
    ax2 = fig.add_subplot(2,2,2)
    ax3 = fig.add_subplot(2,2,3)
    ax4 = fig.add_subplot(2,2,4)
    # preambulo (init settings)
    preambulo_conv_lme_dmax()

    # loop sobre cada parametro dmax
    for coeff in files_dict:
        soluciones = []

        # loop sobre cada archivo asociado a un dmax (coeff)
        for files in files_dict[coeff]:

            # Procesamiento y almacenamiento de data
            soluciones.append(Meshfree_Collocation_Postprocess(files))

        # grafica solucion
        graficar_conv_lme_dmax()

    exportar_conv_lme_dmax()

    return

# :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: #
# :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: #

def CONVERGENCIA(filenames,**kwargs):
    #__________ funciones locales (graficos) __________

    def preambulo_conv_shp():

        # formato fuente (permite utilizar latex, e.g. r'\omega')
        plt.rc('text', usetex=True)
        plt.rc('font', family='serif')

        ## titulo
        #fig.suptitle(r'An\'alisis de convergencia 1D, comparacion funciones de forma')

        # subplot 1, funcion u
        #ax1.set_title(r'Error norma $L2$')
        ax1.set_xlabel(r"Número de puntos")
        ax1.set_ylabel(r'Error norma $L2$, $\epsilon_{L2}$')
        ax1.set_xscale('log')
        #ax1.set_yscale('log')

        # subplot 2, derivada de u
        #ax2.set_title(r'Error norma $H2$')
        ax2.set_xlabel(r"Número de puntos")
        ax2.set_ylabel(r'Error norma $H2$, $\epsilon_{H2}$')
        ax2.set_xscale('log')
        #ax2.set_yscale('log')

        # subplot 3, funcion u
        #ax3.set_title(r'Error norma $L2$')
        ax3.set_xlabel(r'$h_{car}$')
        ax3.set_ylabel(r'Error norma $L2$, $\epsilon_{L2}$')
        #ax3.set_xscale('log')
        #ax3.set_yscale('log')

        # subplot 4, derivada de u
        #ax4.set_title(r'Error norma $H2$')
        ax4.set_xlabel(r'$h_{car}$')
        ax4.set_ylabel(r'Error norma $H2$, $\epsilon_{H2}$')
        #ax4.set_xscale('log')
        #ax4.set_yscale('log')

        return [ax1,ax2,ax3,ax4,fig1,fig2]

    def graficar_conv_shp():

        global axisNum,markers,linestyles,colors

        # seleccionar
        marker    = markers[ axisNum % len(markers) ]
        linestyle = linestyles[ axisNum % len(linestyles) ]
        color     = colors[ axisNum % len(colors) ]
        axisNum += 1

        # agrupa la info
        x_=[] ; y1_=[] ; y2_=[] ; files_=[]
        for sol in soluciones:
            x_.append(sol.nnods)
            y1_.append(sol.L2_norm_error)
            y2_.append(sol.H2_norm_error)
            files_.append(sol.filename) #contiene info

        # ordena la info
        from numpy import argsort
        sort_list=argsort(x_)
        x=[] ; y1=[] ; y2=[] ; files=[]
        for index in sort_list:
            x.append(x_[index])
            y1.append(y1_[index])
            y2.append(y2_[index])
            files.append(files_[index])

        # calculo preliminar (axis 3 y 4)
        x2=[]
        L = max(soluciones[0].coord) - min(soluciones[0].coord)
        for n in x:
            x2.append(L/(n-1)) # se calcula como promedio
                                                 # (puede ser otro criterio)
        h = max(x)-min(x)
        #if ( min(x2) < 1.0 ):
        #   for i in range(len(x2)):
        #       x2[i] += 1.0-min(x2)+h*0.01

        # subplot 1, funcion u
        #ax1.scatter(x,y1,color=color,marker=marker)
        ax1.plot(x,y1,color=color,label=shp)
        ax1.set_xlim(min(x)-h*0.005,max(x)+h*0.1)

        # subplot 2, derivada de u
        #ax2.scatter(x,y2,color=color,marker=marker)
        ax2.plot(x,y2,color=color,label=shp)
        ax2.set_xlim(min(x)-h*0.005,max(x)+h*0.1)

        # considera los 'nlim'-primeros valores (acota grafico con x-log)
        nlim = 0
        h = max(x2[nlim:-1])-min(x2[nlim:-1])

        # subplot 3, funcion u
        #ax3.scatter(x2[nlim:-1],y1[nlim:-1],color=color,marker=marker)
        ax3.plot(x2[nlim:-1],y1[nlim:-1],color=color,label=shp)
        ax3.set_xlim(min(x2[nlim:-1])-h*0.1,max(x2[nlim:-1])+h*0.1)

        # subplot 4, derivada de u
        #ax4.scatter(x2[nlim:-1],y2[nlim:-1],color=color,marker=marker)
        ax4.plot(x2[nlim:-1],y2[nlim:-1],color=color,label=shp)
        ax4.set_xlim(min(x2[nlim:-1])-h*0.1,max(x2[nlim:-1])+h*0.1)

        return

    def exportar_conv_shp():

        # grafica textos (labels) de cada eje
        ax1.legend();ax2.legend();ax3.legend();ax4.legend()

        # ajuste automatico del tamaño de cada subfigura
        #fig.tight_layout()

        #.... crea directorio si es que no existe ....#
        data_dir = "../GRAFICOS/test1d/convergencia/"
        import os
        if ( not os.path.isdir(data_dir) ):
            os.mkdir(data_dir)

        # expotar / mostrar resultados
        shps=[]
        for s in shapes:
            shps.append(s)
        shps = '_'.join(shps)

        file_ = files.split('/')[-1].split('.')[-2].split('_')
        del file_[1] ; del file_[4]
        #file_1 = '_'.join(file_)+'_'+shps+'1.svg'
        file_1 = '_'.join(file_)+'_'+shps+'1.pdf'
        #file_2 = '_'.join(file_)+'_'+shps+'2.svg'
        file_2 = '_'.join(file_)+'_'+shps+'2.pdf'
        fig1.savefig(data_dir+file_1)
        fig2.savefig(data_dir+file_2)

        show = kwargs.get('show',False)
        if (show):
            plt.show()

        plt.cla()   # clear axis
        plt.clf()   # clear figure
        plt.close() # close a figure windows

        return

    # __________ funcion principal __________

    # contar numero de funciones de forma
    filenames = filenames.split(',')
    shapes = []
    for files in filenames:
        shp = files.split('/')[-1].split('_')[5]
        if ( not shp in shapes ):
            shapes.append(shp)

    # separar filenames segun funcion de forma
    files_dict={}
    for shp in shapes:
        files_dict[shp] = []
    for files in filenames:
        shp = files.split('/')[-1].split('_')[5]
        files_dict[shp].append(files)

    # genera objetos matplotlib (fig y axis)
    import matplotlib.pyplot as plt
    fig1=plt.figure(figsize=(6,7))
    ax1 = fig1.add_subplot(2,1,1)
    ax2 = fig1.add_subplot(2,1,2)

    fig2=plt.figure(figsize=(6,7))
    ax3 = fig2.add_subplot(2,1,1)
    ax4 = fig2.add_subplot(2,1,2)

    # preambulo (init settings)
    preambulo_conv_shp()

    # loop sobre cada parametro dmax
    for shp in files_dict:
        soluciones = []

        # loop sobre cada archivo asociado a un dmax (coeff)
        for files in files_dict[shp]:

            # Procesamiento y almacenamiento de data
            #soluciones.append(processing_data(files))
            soluciones.append(Meshfree_Collocation_Postprocess(files))

        # grafica solucion
        graficar_conv_shp()

    exportar_conv_shp()

    return

# :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: #
# :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: #

def GRAFICAR_SOLUCION_1D(filename,**kwargs):

    data=Meshfree_Collocation_Postprocess(filename)

    show = kwargs.get('show',False)
    data.Graficar(show=show)

    return

# :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: #
# :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: #

def SHAPE_FUNCTION_PLOT(filename_input,**kwargs):

    # identificar cuantos archivos hay
    filenames = filename_input.split(',')
    n_files = len(filenames)

    shapefcn_list = []
    for files in filenames:

        # recopilacion de la informacion de la func. de forma y
        # lectura del archivo log (data de la calidad de la nube)
        shapefcn_list.append( Shape_Function_Postprocess(files) )

    # graficar funcion de forma
    show = kwargs.get('show',False)
    Graficar_Funcion_de_Forma(shapefcn_list,show)

    return

def Graficar_Funcion_de_Forma(shpfcn_list,show):

    # Genera dos graficos:
    # 1 . un grafico enfocado a cada función de forma y sus
    # derivadas respecto a un punto
    # 2. la totalidad de las funciones de forma en un grafico
    # que incluya todos los puntos de discretización del
    # dominio

    # ---------- Definiciones globales (para graficar) -------

    global axisNum,markers,linestyles,colors

    # ---------- Preambulo MatPlotLib -----------------------
    import matplotlib.pyplot as plt

    plt.rc('text', usetex=True)
    plt.rc('font', family='serif')

    # ------------- Plot ShpFcn_List ------------------------

    # tolerancias exigidas
    errtol0 = shpfcn_list[0].errtol0
    errtol1 = shpfcn_list[0].errtol1
    errtol2 = shpfcn_list[0].errtol2

    sf_a_graficar=5
    salto = int(shpfcn_list[0].colloc_npt/sf_a_graficar)
    for pos in range(0,shpfcn_list[0].colloc_npt,salto):

        # crea figuras
        fig=plt.figure(figsize=(10,6))

        # extraer informacion de filename
        filename = shpfcn_list[0].filename
        filename_data = shpfcn_list[0].filename.split('/')[-1].split('_')
        test = filename_data[0]
        npts = filename_data[1]
        dist = filename_data[2]
        type = filename_data[3]
        shpf = filename_data[4]
        schm = filename_data[5].split('.')[0] # recorta '.dat'

        fig.suptitle(r'Funcion de forma test '+test+' npts '+npts+\
        ', fun.forma '+shpf , y=1.0)

        #left = 0.125  # the left side of the subplots of the figure
        #right = 0.9   # the right side of the subplots of the figure
        #bottom = 0.1  # the bottom of the subplots of the figure
        #top = 0.9     # the top of the subplots of the figure
        #wspace = 0.2  # the amount of width reserved for space between subplots,
        #              # expressed as a fraction of the average axis width
        #hspace = 0.2  # the amount of height reserved for space between subplots,
        #              # expressed as a fraction of the average axis height
        plt.subplots_adjust(left=0.125, bottom=0.1, right=0.9, top=0.9, wspace=0.2, hspace=0.2)

        # crea ejes
        ax1 = fig.add_subplot(3,1,1)
        ax2 = fig.add_subplot(3,1,2)
        ax3 = fig.add_subplot(3,1,3)

        ax1.text(0.01,0.75, r"Tolerancia solicitada : "+str(errtol0)+'\n'\
            +r"Test :$\sum_{i=1}^{np} F(x_i) \phi_i(x_{star}) - F(x_i)$, $F(x)=x^2$"\
            ,transform=ax1.transAxes,size='small')
        ax2.text(0.01,0.75, r"Tolerancia solicitada : "+str(errtol1)+'\n'\
            +r"Test :$\sum_{i=1}^{np} F'(x_i) \phi'_i(x_{star}) - F'(x_i)$, $F(x)=x^2$"\
            ,transform=ax2.transAxes,size='small')
        ax3.text(0.01,0.75, r"Tolerancia solicitada : "+str(errtol2)+'\n'\
            +r"Test :$\sum_{i=1}^{np} F''(x_i) \phi''_i(x_{star}) - F''(x_i)$, $F(x)=x^2$"\
            ,transform=ax3.transAxes,size='small')

        axisNum = 0

        for shpfcn in shpfcn_list:

            # seleccionar
            marker    = markers[ axisNum % len(markers) ]
            linestyle = linestyles[ axisNum % len(linestyles) ]
            color     = colors[ axisNum % len(colors) ]
            axisNum += 1

            # desempaquetar phi data
            phi_,dphi_,ddphi_ = zip(*shpfcn.phi)
            phi_=phi_[pos] ; dphi_=dphi_[pos] ; ddphi_=ddphi_[pos]
            errvl = shpfcn.errvl ; errvl0,errvl1,errvl2 = zip(*errvl)
            errlg = shpfcn.errlg ; errlg0,errlg1,errlg2 = zip(*errlg)
            filename = shpfcn.filename
            filename_data = shpfcn.filename.split('/')[-1].split('_')

            test = filename_data[0]
            npts = filename_data[1]
            dist = filename_data[2]
            type = filename_data[3]
            shpf = filename_data[4]
            schm = filename_data[5].split('.')[0] # recorta '.dat'

            # agrega informacion especifica segun shape function
            if ( shpf == 'lme-v1'):
                label = shpf.upper()+r' (rmax='+filename_data[-1][:-4]+')'
            else:
                label = shpf.upper()

            if ( schm == 'direct' ):
                coord = shpfcn.colloc_coord
            elif ( schm == 'least-squares' ):
                coord = shpfcn.source_coord
            else:
                print('schm no reconocido')
                print('schm : ',schm)
                return

            xnear_ = []
            for pt in shpfcn.cld[pos][-1]:
                xnear_.append(coord[pt])

            y_aux=[ 0.0 for j in range(len(coord)) ]

            # ordenar x de menor a mayor para plotear lineas
            from numpy import argsort
            sort_list=argsort(xnear_)
            xnear=[] ; phi=[] ; dphi=[] ; ddphi=[]
            for index in sort_list:
                xnear.append(xnear_[index])
                phi.append(phi_[index])
                dphi.append(dphi_[index])
                ddphi.append(ddphi_[index])

            # warning color = 'red' si no supera la tolerancia
            warning_color0 = 'black'
            warning_color1 = 'black'
            warning_color2 = 'black'
            if ( errlg0[pos] ) :
                warning_color0='red'
            if ( errlg1[pos] ) :
                warning_color1='red'
            if ( errlg2[pos] ) :
                warning_color2='red'

            # subplot 1, funcion phi
            ax1.set_title(r'Funcion $\phi$')
            ax1.set_xlabel(r'$x$')
            ax1.set_ylabel(r'$\phi(x)$')
            ax1.scatter(xnear,phi,label=label,color=color)
            ax1.plot(xnear,phi,color=color)
            ax1.scatter(coord,y_aux,s=5)
            ax1.text(0.01,0.75-axisNum*0.1,label+' : '+str(errvl0[pos]) \
                    ,transform=ax1.transAxes,color=warning_color0,size='small')

            # subplot 2, derivada de phi
            ax2.set_title(r'Funcion $\partial \phi / \partial x$')
            ax2.set_xlabel(r'$x$')
            ax2.set_ylabel(r'$\frac{\partial \phi}{\partial x}$')
            ax2.scatter(xnear,dphi,label=label,color=color)
            ax2.plot(xnear,dphi,color=color)
            ax2.scatter(coord,y_aux,s=5)
            ax2.text(0.01,0.75-axisNum*0.1,label+' : '+str(errvl1[pos]) \
                    ,transform=ax2.transAxes,color=warning_color1,size='small')

            # subplot 3, segunda derivada de phi
            ax3.set_title(r'Funcion $\partial^2 \phi / \partial x^2$')
            ax3.set_xlabel(r'$x$')
            ax3.set_ylabel(r'$\frac{\partial^2 \phi}{\partial x^2}$')
            ax3.scatter(xnear,ddphi,label=label,color=color)
            ax3.plot(xnear,ddphi,color=color)
            ax3.scatter(coord,y_aux,s=5)
            ax3.text(0.01,0.75-axisNum*0.1,label+' : '+str(errvl2[pos]) \
                    ,transform=ax3.transAxes,color=warning_color2,size='small')

        # ajuste automatico del tamaño de cada subfigura
        fig.tight_layout()

        # grafica textos (labels) de cada eje
        ax1.legend(loc=1) ; ax2.legend(loc=1) ; ax3.legend(loc=1) #loc=1 upper right

        #.... crea directorio si es que no existe ....#
        data_dir = "../GRAFICOS/test1d/sf/"
        import os
        if ( not os.path.isdir(data_dir) ):
            os.mkdir(data_dir)

        # expotar / mostrar resultados
        #newfile = filename.split('/')[-1:][0].replace('.dat','_'+str(pos)+'.svg')
        newfile = filename.split('/')[-1:][0].split('_')
        del newfile[-2]
        newfile = '_'.join(newfile)
        newfile = newfile.replace('.dat','_'+str(pos)+'.svg')
        #newfile = newfile.replace('.dat','_'+str(pos)+'.pdf')

        plt.savefig(data_dir+newfile)

        if (show):
            plt.show()

        plt.cla()   # clear axis
        plt.clf()   # clear figure
        plt.close() # close a figure windows

    return

# :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: #
# :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: #

class Shape_Function_Postprocess(object):

    def __init__( self, filename ):
        # importar informacion
        self.filename = filename
        # lectura de colloc_coord y source_coord
        self.lectura_de_geometria()
        # lectura de nube y funcion de forma
        self.lectura_funcion_de_forma()
        # lectura del archivo log
        self.lectura_archivo_log()

    def lectura_archivo_log(self):
        file_ = self.filename.replace('nubes','log')
        # apertura archivo -> lectura de data
        f = open(file_,'r')
        contents = f.read()
        f.close()
        # crea elemento iterable / lista 'file_as_lines'
        file_as_list = contents.splitlines()

        self.errtol0 = float(file_as_list[1].split()[-1])
        self.errtol1 = float(file_as_list[2].split()[-1])
        self.errtol2 = float(file_as_list[3].split()[-1])

        c = 11
        err_log=[] ; err_val=[] ; indx=[] #almacena localmente
        for i in range(self.colloc_npt):  #las variables
            indx.append(int(file_as_list[c+1]))
            err_log.append(file_as_list[c+2])
            err_val.append(file_as_list[c+3])
            c += 3

        # almacenar en variable global
        self.errlg = []
        for err in err_log:
            split = err.split() ; aux = []
            for splt in split:
                if ( splt == 'T' ):
                    aux.append(True)
                elif ( splt == 'F' ):
                    aux.append(False)
                else:
                    # OJO AHI!
                    aux.append(False)
            self.errlg.append(aux)

        self.errvl = []
        for err in err_val:
            split = err.split() ; aux = []
            for splt in split:
                aux.append(float(splt))
            self.errvl.append(aux)

        return

    def lectura_de_geometria(self):
        # ------------------------------------
        # OBS: el nombre del archivo a leer se
        # desprende del nombre del archivo que
        # contiene los datos de la func.forma.
        # ------------------------------------
        file_ = self.filename
        file_ = file_.split('/')[-1]
        file_ = '_'.join(file_.split('_')[:4])
        file_ = file_+'.dat'
        geo_dir  = '../DATOS/test1d/preproceso/'
        geo_file = geo_dir+file_

        # apertura archivo -> lectura de data
        f = open(geo_file,'r')
        contents = f.read()
        f.close()

        # crea elemento iterable / lista 'file_as_lines'
        file_as_list = contents.splitlines()

        c = 0 ; self.colloc_coord=[]
        self.colloc_npt = int(file_as_list[c].split(' ')[0])
        for i in range(self.colloc_npt):
            c+=1
            self.colloc_coord.append(float(file_as_list[c].split()[0]))

        c+=4 ; self.source_coord=[]
        self.source_npt = int(file_as_list[c].split(' ')[0])
        for i in range(self.source_npt):
            c+=1
            self.source_coord.append(float(file_as_list[c].split()[0]))

        return

    def lectura_funcion_de_forma(self):

        # informacion asociado a cada punto
        self.cld = []
        self.phi = []

        # apertura archivo -> lectura de data
        f = open(self.filename,'r')
        contents = f.read()
        f.close()

        file_as_list = contents.splitlines()

        c = 11
        aux = int(file_as_list[c].split(' ')[0])

        if ( aux != self.colloc_npt ):
            self.ierror = 1
            print('numero de puntos no son iguales')
            print(' self.colloc_np (geometria)  : ',self.colloc_np)
            print(' self.colloc_np (func.forma) : ',aux)
            print()
            return

        c+=1

        for i in range(self.colloc_npt):

            # cld = ( np_cloud , [ pt_list ] )
            aux = []
            for item in file_as_list[c+2].split():
                aux.append(int(item)-1)
            aux = ( int(file_as_list[c+1]) , aux )
            self.cld.append( aux )

            # shp = ( [ phi ] , [ dphi ] , [ ddphi ] )
            aux1 = file_as_list[c+3].split()
            aux2 = file_as_list[c+4].split()
            aux3 = file_as_list[c+5].split()
            for j in range(self.cld[-1][0]):
                aux1[j] = float(aux1[j])
                aux2[j] = float(aux2[j])
                aux3[j] = float(aux3[j])
            aux = ( aux1, aux2, aux3 )
            self.phi.append( aux )
            c+=6

        return

class Meshfree_Collocation_Postprocess(object):

    def __init__( self,filename ):
        # importa la informacion
        coord,u_app,du_app,u_exa,du_exa = self.importar_data(filename)
        # asigna data a las variables globales correspondientes
        self.coord     = coord
        self.u_approx  = u_app
        self.du_approx = du_app
        self.u_exact   = u_exa
        self.du_exact  = du_exa
        self.filename  = filename
        # calcula el error global
        self.Global_Error()

    def importar_data(self,filename):
        # ---------------------------------------#
        # informacion obtenida:                  #
        # xcoord, u_approx(1:2), u_exact(1:2)    #
        # ---------------------------------------#

        # apertura archivo -> lectura de data
        f = open(filename,'r')
        contents = f.read()
        f.close()

        # crea elemento iterable / lista 'file_as_lines'
        file_as_list = contents.splitlines()

        npoin = int(file_as_list[0])

        # lectura de data: xcoord(i), u_approx(i,1:2), u_exact(i,1:2)
        auxstring=[]
        for i in range(npoin):
            auxstring.append( file_as_list[i+1].split() )

        # traspaso hacia las variables correspondientes
        x, u_app, du_app, u_exa, du_exa = zip(*auxstring)

        # transformacion de string a float
        x = [ float(coord) for coord in x ]
        u_app = [ float(u) for u in u_app ]
        u_exa = [ float(u) for u in u_exa ]
        du_app = [ float(u) for u in du_app ]
        du_exa = [ float(u) for u in du_exa ]

        return [x,u_app,du_app,u_exa,du_exa]

    def Global_Error(self):
            #L2 norm error
            nnods = len(self.coord)
            self.nnods = nnods
            sum1 = 0 ; sum2 = 0
            for i in range(nnods):
                sum1 += pow(self.u_approx[i] - self.u_exact[i],2)
                sum2 += pow(self.u_exact[i],2)
            self.L2_norm_error = pow(sum1/sum2,0.5)
            #H2 norm error
            sum1 = 0 ; sum2 = 0
            for i in range(nnods):
                sum1 += pow(self.du_approx[i] - self.du_exact[i],2)
                sum2 += pow(self.du_exact[i],2)
            self.H2_norm_error = pow(sum1/sum2,0.5)
            return

    def Graficar(self,show):

        # extraer informacion de filename
        filename = self.filename
        filename_data = self.filename.split('/')[-1].split('_')
        test = filename_data[0]
        npts = filename_data[1]
        dist = filename_data[2]
        caso = filename_data[4]
        shpf = filename_data[5]

        import matplotlib.pyplot as plt

        # formato fuente (permite utilizar latex, e.g. r'\omega')
        plt.rc('text', usetex=True)
        plt.rc('font', family='serif')

        # crea figuras
        fig=plt.figure()
        #fig=plt.figure(figsize=(6,10))

        # titulo
        if (dist=='irreg'):
            dist='irregular'
        if (caso=='caso-1'):
            caso='cond.contorno Dirichlet-Dirichlet'
        elif (caso=='caso-2'):
            caso='cond.contorno Dirichlet-Neumann'
        elif (caso=='caso-3'):
            caso='cond.contorno Neumann-Dirichlet'

        #fig1.suptitle(r'Solucion test '+test+' npts '+npts+\
        #', fun.forma '+shpf+'\n '+caso , y=1.0)
        #fig2.suptitle(r'Solucion test '+test+' npts '+npts+\
        #', fun.forma '+shpf+'\n '+caso , y=1.0)

        # crea ejes
        ax1 = fig.add_subplot(2,1,1)
        ax2 = fig.add_subplot(2,1,2)

        # subplot 1, funcion u
        ax1.set_title(r'Solución numérica $u=u(x)$')
        ax1.set_xlabel(r'$x$')
        ax1.set_ylabel(r'$u(x)$')
        ax1.scatter(self.coord,self.u_approx)

        # subplot 2, derivada de u
        ax2.set_title(r'Solución numérica $\partial u / \partial x$')
        ax2.set_xlabel(r'$x$')
        ax2.set_ylabel(r'$\frac{\partial u}{\partial x}$')
        ax2.scatter(self.coord,self.du_approx)

        # solucion exacta
        from preproceso_1d import exact_sol
        from numpy import arange
        test = filename.split('/')[-1:][0].split('_')[0]
        length = max(self.coord)-min(self.coord)
        step = length/100.
        xplot=arange(min(self.coord),max(self.coord),step)
        uexact,duexact = exact_sol(test,xplot)
        ax1.plot(xplot,uexact)
        ax2.plot(xplot,duexact)

        #.... crea directorio si es que no existe ....#
        data_dir = "../GRAFICOS/test1d/solucion/"
        import os
        if ( not os.path.isdir(data_dir) ):
            os.mkdir(data_dir)

        plt.tight_layout()

        # expotar / mostrar resultados
        #newfile = filename.split('/')[-1:][0].replace('.dat','.svg')
        newfile = filename.split('/')[-1:][0].replace('.dat','.pdf')
        fig.savefig(data_dir+newfile)

        if (show):
            plt.show()

        return

#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::


from typing import List, Dict
import torch
from torch import tensor
from torch._C import device
from torch_utils import pi

def calculate_tangent_rots(
    subdivision_level: List[int],
    device: torch.device = torch.device("cpu")
    ) -> List[Dict[str, float]]:
    """ Calculate rotation parameters for transformation

        params:
            subdivision_level (int): set number of faces, which equals to:
                    num_of_faces = 20*(4^b), where b=subdivision_level
        returns:
            rots (List[[dict]]): list of dicts with rots parameters.

    """
    rots = [[] for _ in subdivision_level]
    radius = 10
    for i, sub_lvl in enumerate(subdivision_level):
        faces = init_20_faces(radius, device=device)
        for _ in range(0, sub_lvl):
            faces = subdivide_faces(faces, radius, device=device)
        angles = calculate_angles(faces, device=device)
        for ang in angles:
            rots[i].append(
                {
                    'roll': 0.,
                    'pitch': ang[1],
                    'yaw': ang[0]
                }
            )
    return rots

def calculate_tangent_angles(
    subdivision_level: List[int],
    device: torch.device = torch.device("cpu")
    ) -> List[torch.Tensor]:
    """ Calculate angles to icosahedron faces

        params:
            subdivision_level (int): set number of faces, which equals to:
                    num_of_faces = 20*(4^b), where b=subdivision_level
        returns:
            angles (List[[np.ndarray]]): list of angles.

    """
    angles = [[] for _ in subdivision_level]
    radius = 10
    for i, sub_lvl in enumerate(subdivision_level):
        faces = init_20_faces(radius, device=device)
        for _ in range(0, sub_lvl):
            faces = subdivide_faces(faces, radius, device=device)
        angles[i] = calculate_angles(faces, device=device)
    return angles

def calculate_angles(
    faces: torch.Tensor,
    device: torch.device=torch.device("cpu")
    ) -> torch.Tensor:
    """ Calculate angles from (0,0,0) point to centroid of input faces

        params:
            faces: array of triplets with coordinates of icosahedron faces. Each face
            represented as array of shape [n,3,3], where n - number of faces.

        returns:
            angles: array of longtitude and latitude angles to input faces. Returns
            array of shape [n,2]

    """
    centroids = calculate_centroids(faces, device)
    angles = torch.zeros(centroids.shape[0], 2, device=device)
    for i, c in enumerate(centroids):
        lat = calculate_lat(c, tensor([c[0], c[1], 0], dtype=torch.float32, device=device))
        lon = calculate_lon(c[:2], tensor([0,1], dtype=torch.float32, device=device))
        angles[i] = tensor([lon, lat])
    angles[:, 1] *= torch.sign(centroids[:, 2])
    return angles * -1

def subdivide_faces(
    faces: torch.Tensor,
    radius: int=10,
    device: torch.device = torch.device("cpu")
    ):
    """ Subdivide faces of icosahedron on next level.

        params:
            faces: array of triplets with coordinates of icosahedron faces. Each face
            represented as array of shape [n,3,3], where n - number of faces.

        returns:
            new_faces: array of subdivided faces. Returns array of shape [n*4,3,3]

    """
    new_faces = torch.zeros(faces.shape[0]*4,3,3, device=device)
    for i, face in enumerate(faces):
        v1,v2,v3 = face

        v1_new = __computeHalfVertex(v1, v2)
        v2_new = __computeHalfVertex(v2, v3)
        v3_new = __computeHalfVertex(v1, v3)

        # add 4 new triangles to vertex array
        new_faces[i*4] = torch.vstack([v1,    v1_new, v3_new])
        new_faces[i*4+1] = torch.vstack([v1_new, v2,    v2_new])
        new_faces[i*4+2] = torch.vstack([v1_new, v2_new, v3_new])
        new_faces[i*4+3] = torch.vstack([v3_new, v2_new, v3])
    return new_faces

def init_icosahedron_vertices(
    radius: int = 10,
    device: torch.device = torch.device("cpu")
    ) -> torch.Tensor:
    """ Calculate icosahedron vertices at zero subdivision_level.
        Used for icosahedron initialization

        params:
            radius: radius of icosahedron sphere. Can be removed with constant in all methods.

        returns:
            vertices: points of initialized icosahedron, array of shape [20,3]

    """
    PI, r = pi.to(device), radius
    H_ANGLE = torch.clone(PI) / 180 * 72
    V_ANGLE = torch.arctan(tensor(1.0 / 2, device=device))
    vertices = torch.zeros(12,3, device=device)
    hAngle1, hAngle2 = -PI / 2 - H_ANGLE / 2, -PI / 2

    vertices[0] = tensor([0,0,r], device=device)
    vertices[-1] = tensor([0,0,-r], device=device)

    xy,z = torch.ones(10,2, device=device), torch.ones(10,1, device=device)
    xy *= r*torch.cos(V_ANGLE)
    z *= r*torch.sin(V_ANGLE)
    h1 = (torch.arange(0,5, device=device) * H_ANGLE) + hAngle1
    h2 = (torch.arange(0,5, device=device) * H_ANGLE) + hAngle2
    xy[:5,0] = xy[:5,0] * torch.cos(h1)
    xy[:5,1] = xy[:5,1] * torch.sin(h1)
    xy[5:,0] = xy[5:,0] * torch.cos(h2)
    xy[5:,1] = xy[5:,1] * torch.sin(h2)
    z[5:] = -z[5:]
    xyz = torch.hstack([xy,z])

    vertices[1:-1] = xyz
    return vertices

def init_20_faces(
    radius: int = 10,
    device: torch.device = torch.device("cpu")
    ):
    """ Calculate icosahedron faces at zero subdivision level.
        Used for icosahedron initialization

        params:
            radius: radius of icosahedron sphere. Can be removed with constant in all methods.

        returns:
            faces: coordinates of icosahedron faces, array of shape [20,3,3]

    """
    vertices = init_icosahedron_vertices(radius, device)
    faces = torch.zeros(20,3,3, device=device)
    upper_pent = vertices[1:6]
    down_pent = vertices[-6:]
    for i in range(1, 6):
        faces[i-1] = torch.vstack([vertices[0], upper_pent[i%5], upper_pent[i-1]])
        faces[-i] = torch.vstack([vertices[-1], down_pent[i%5], down_pent[i-1]])
    # head, middle and tail pointers for triangles
    ptr_h, ptr_m, ptr_t = -1, 0, 0
    for i in range(1, 11):
        if i%2 != 0:
            ptr_h += 1
            ptr_t += 1
            faces[i+4] = torch.vstack([upper_pent[ptr_h], down_pent[ptr_m%5], upper_pent[ptr_t%5]]).reshape((1,3,3))
        if i%2 == 0:
            ptr_m += 1
            faces[i+4] = torch.vstack([down_pent[ptr_h], upper_pent[ptr_m%5], down_pent[ptr_t%5]]).reshape((1,3,3))
    return faces

def calculate_centroids(
    faces: torch.Tensor,
    device: torch.device = torch.device("cpu")
    ):
    """ Calculate centroids of faces

        params:
            faces: array of faces

        returns:
            centroids: array of centroid cordinates for input faces. Output array
            with shape of [n,3], where n - number of input faces
    """
    centroids = torch.zeros((faces.shape[0],3), device=device)
    for i, face in enumerate(faces):
        centroids[i] = face.sum(axis=0)/3
    return centroids

def __computeHalfVertex(v1, v2, radius:int = 10):
    """ Compute half of icosahedron vertices

        params:
            v1,v2 (np.ndarray): two vertices points
            radius: radius of icosahedron sphere. Can be removed with constant in all methods.

        returns:
            new_v (np.ndarray): coordinates of half verice scaled on scale factor
    """
    new_v = v1+v2
    scale = radius / torch.sqrt(torch.sum(new_v**2))
    return new_v * scale

def calculate_lat(
    vec1: torch.Tensor,
    vec2: torch.Tensor,
    device: torch.device = torch.device("cpu")
    ):
    """ Calculate latitude between two vectors
    """
    unit_vector_1 = (vec1 / torch.linalg.norm(vec1)).to(device)
    unit_vector_2 = (vec2 / torch.linalg.norm(vec2)).to(device)
    dot_product = torch.dot(unit_vector_1, unit_vector_2)
    return torch.arccos(dot_product)

def calculate_lon(
    vec1: torch.Tensor,
    vec2: torch.Tensor
    ):
    """ Calculate longtitude between two vectors
    """
    dot = torch.dot(vec1, vec2)
    det = torch.linalg.det(torch.vstack([vec1, vec2]))
    return torch.atan2(det, dot)


#!/usr/bin/env python3
#./entrypoint.py

#
# CODE INJECTED FROM: 220-install/.entrypoint.py.HEAD
#

#
#   Copyright 2019 Alexander Gabert
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

"""
This is the entrypoint script for the 220-install installer container.
Please remember that this container does not run the service itself.
It merely installs it on a remote machine defined by the metadata model.

Your servers must be configured in conf/roles.yaml and conf/servers.yaml.
"""

import os
import sys
import time

from prometheus_client import start_http_server, Summary, Gauge

from reykjavik.config import ConfigManager
from reykjavik.deps import DependencyManager

PRE_INSTALL_TIME_LABEL_SUPERCLASS = Summary( \
    'pre_install_processing_seconds', \
    'Time spent in PRE_INSTALL phase', \
    ('app', 'server', 'service', 'cluster', 'domain'))

PRE_INSTALL_TIME = PRE_INSTALL_TIME_LABEL_SUPERCLASS.labels( \
    "reykjavik", \
    os.environ["SERVER"], \
    os.environ["SERVICE"], \
    os.environ["CLUSTER"], \
    os.environ["DOMAIN"])

PRE_INSTALL_ERRORS_LABEL_SUPERCLASS = Gauge( \
    'pre_install_errors', \
    'Errors during PRE_INSTALL phase', \
    ('app', 'server', 'service', 'cluster', 'domain'))

PRE_INSTALL_ERRORS = PRE_INSTALL_ERRORS_LABEL_SUPERCLASS.labels( \
    "reykjavik", \
    os.environ["SERVER"], \
    os.environ["SERVICE"], \
    os.environ["CLUSTER"], \
    os.environ["DOMAIN"])

INSTALL_TIME_LABEL_SUPERCLASS = Summary( \
    'install_processing_seconds', \
    'Time spent in INSTALL phase', \
    ('app', 'server', 'service', 'cluster', 'domain'))

INSTALL_TIME = INSTALL_TIME_LABEL_SUPERCLASS.labels( \
    "reykjavik", \
    os.environ["SERVER"], \
    os.environ["SERVICE"], \
    os.environ["CLUSTER"], \
    os.environ["DOMAIN"])

INSTALL_ERRORS_LABEL_SUPERCLASS = Gauge( \
    'install_errors', \
    'Errors during INSTALL phase', \
    ('app', 'server', 'service', 'cluster', 'domain'))

INSTALL_ERRORS = INSTALL_ERRORS_LABEL_SUPERCLASS.labels( \
    "reykjavik", \
    os.environ["SERVER"], \
    os.environ["SERVICE"], \
    os.environ["CLUSTER"], \
    os.environ["DOMAIN"])

POST_INSTALL_TIME_LABEL_SUPERCLASS = Summary( \
    'post_install_processing_seconds', \
    'Time spent in POST_INSTALL phase', \
    ('app', 'server', 'service', 'cluster', 'domain'))

POST_INSTALL_TIME = POST_INSTALL_TIME_LABEL_SUPERCLASS.labels( \
    "reykjavik", \
    os.environ["SERVER"], \
    os.environ["SERVICE"], \
    os.environ["CLUSTER"], \
    os.environ["DOMAIN"])

POST_INSTALL_ERRORS_LABEL_SUPERCLASS = Gauge( \
    'post_install_errors', \
    'Errors during POST_INSTALL phase', \
    ('app', 'server', 'service', 'cluster', 'domain'))

POST_INSTALL_ERRORS = POST_INSTALL_ERRORS_LABEL_SUPERCLASS.labels( \
    "reykjavik", \
    os.environ["SERVER"], \
    os.environ["SERVICE"], \
    os.environ["CLUSTER"], \
    os.environ["DOMAIN"])

HEALTH_CHECK_TIME_LABEL_SUPERCLASS = Summary( \
    'health_check_processing_seconds', \
    'Time spent in HEALTH_CHECK phase', \
    ('app', 'server', 'service', 'cluster', 'domain'))

HEALTH_CHECK_TIME = HEALTH_CHECK_TIME_LABEL_SUPERCLASS.labels( \
    "reykjavik", \
    os.environ["SERVER"], \
    os.environ["SERVICE"], \
    os.environ["CLUSTER"], \
    os.environ["DOMAIN"])

HEALTH_CHECK_ERRORS_LABEL_SUPERCLASS = Gauge( \
    'health_check_errors', \
    'Errors during HEALTH_CHECK phase', \
    ('app', 'server', 'service', 'cluster', 'domain'))

HEALTH_CHECK_ERRORS = HEALTH_CHECK_ERRORS_LABEL_SUPERCLASS.labels( \
    "reykjavik", \
    os.environ["SERVER"], \
    os.environ["SERVICE"], \
    os.environ["CLUSTER"], \
    os.environ["DOMAIN"])

#
# CODE INJECTED FROM 220-install/entrypoint.py.PRE_INSTALL
#

@PRE_INSTALL_TIME.time()
def pre_install():
    """
    pre-installation step
    """
    errors = 0

    server = os.environ["SERVER"]

    errors = errors + DependencyManager().block(service="210-configure")

    #
    # Open Source MidoNet Repository
    #
    result = ConfigManager().upload_as_file(server, "/etc/apt/sources.list.d/midonet.list", """

#
# Midonet Plugin for OpenStack Queens is at this location
#
deb http://builds.midonet.org/openstack-queen stable main

#
# MidoNet agent and cluster
#
deb http://builds.midonet.org/midonet-5.6 stable main

#
# MidoNet additional tools
#
deb http://builds.midonet.org/misc stable main

""")

    errors = errors + result

    #
    # Datastax Repository for Cassandra
    #
    result = ConfigManager().upload_as_file(server, "/etc/apt/sources.list.d/datastax.list", """

#
# Datastax Cassandra
#
deb http://debian.datastax.com/community 2.2 main

    """)

    errors = errors + result

    errors = errors + ConfigManager().get_connection(server).run("""

set -e

#
# Load and accept the datastax key
#
wget -SO- https://debian.datastax.com/debian/repo_key | apt-key add -

#
# Load and accept the midonet repo key
#
wget -SO- https://builds.midonet.org/midorepo.key | apt-key add -

#
# Prevent grub from sabotaging non-interactive dist-upgrades
#
echo "grub grub/update_grub_changeprompt_threeway select install_new" | debconf-set-selections
echo "grub-legacy-ec2 grub/update_grub_changeprompt_threeway select install_new" | debconf-set-selections

""").exited

    return errors

#
# CODE INJECTED FROM 220-install/entrypoint.py.INSTALL
#

@INSTALL_TIME.time()
def install():
    """
    installation step
    """
    return ConfigManager().get_connection(os.environ["SERVER"]).run("""

set -e

apt-get update

export DEBIAN_FRONTEND=noninteractive

#
# Try to repair incomplete previous installations.
#
yes | dpkg --configure -a
yes | apt-get -y -u --force-yes install
yes | apt-get -y -u --force-yes dist-upgrade

#
# do not add curl to this list, it conflicts with libcurl3 which is needed for midolman
#
apt-get -y -u install software-properties-common \
    wget \
    tcpdump \
    nload \
    ntpdate \
    sharutils \
    crudini \
    htop \
    glances \
    vim \
    screen \
    tmux \
    mosh \
    openjdk-8-jre-headless \
    python-openstackclient \
    python-midonetclient \
    ntp \
    jq

""").exited

#
# CODE INJECTED FROM 220-install/entrypoint.py.POST_INSTALL
#

@POST_INSTALL_TIME.time()
def post_install():
    """
    post-installation step
    """
    return ConfigManager().get_connection(os.environ["SERVER"]).run("""

apt-get clean
apt-get -y autoremove

mkdir -pv /root
touch /root/.cloud-warnings.skip

mkdir -pv /var/lib/cloud/instance/warnings
touch /var/lib/cloud/instance/warnings/.skip

""").exited

#
# CODE INJECTED FROM 220-install/entrypoint.py.HEALTH_CHECK
#

@HEALTH_CHECK_TIME.time()
def health_check():
    """
    health check
    """
    return ConfigManager().get_connection(os.environ["SERVER"]).run("dpkg -l").exited

#
# CODE INJECTED FROM: SERVICE/.entrypoint.py.TAIL
#

def main():
    """
    Main function called by docker container.
    """
    server = os.environ["SERVER"]
    service = os.environ["SERVICE"]
    print("{0}: starting for server: {1} service: {2}".format(sys.argv[0], server, service))

    start_http_server(80)

    print("checking roles ...")
    for role in ConfigManager().roles:
        if server in ConfigManager().roles[role]:
            print("server: {0} role: {1}".format(server, role))

    errors = 0

    #
    # update the prometheus endpoint for phase PRE_INSTALL
    #
    pre_install_errors = pre_install()
    PRE_INSTALL_ERRORS.set(pre_install_errors)
    errors = errors + pre_install_errors

    #
    # update the prometheus endpoint for phase INSTALL
    #
    install_errors = install()
    INSTALL_ERRORS.set(install_errors)
    errors = errors + install_errors

    #
    # update the prometheus endpoint for phase POST_INSTALL
    #
    post_install_errors = post_install()
    POST_INSTALL_ERRORS.set(post_install_errors)
    errors = errors + post_install_errors

    #
    # Restart the container if any error happened so far.
    #
    if errors > 0:
        return "errors in installation phases."

    #
    # Restart the install container if the health check fails more than 2 times.
    #
    while True:
        health_check_errors = 0

        for _ in range(10):
            if health_check() > 0:
                health_check_errors = health_check_errors + 1
            HEALTH_CHECK_ERRORS.set(health_check_errors)
            time.sleep(6)

        if health_check_errors > 2:
            return "too many health check errors."

        DependencyManager().unblock()
        time.sleep(60)

#
# Docker container entrypoint.py
#
if __name__ == "__main__":
    sys.exit(main())


import os
import os.path
import re

AIM_DIRS = {
    "./CPP/"   :"CPP"   ,
    './Python/':"Python",
    "./MySql"  :"MySQL"
}

class Topic:
    def __init__(self,dirName,fileName):
        self.index = fileName.split(".")[0]
        parts = self.index.split()
        if len(parts) > 0:
            self.index = parts[0]
            self.title = parts[-1]
        else:
            self.title = ""
        self.path = os.path.join(dirName,fileName)
        self.path = self.path.replace(" ","%20")
        self.language = AIM_DIRS[dirName]

def indexCmp(x):
    index = x[0]
    if index.isdigit():
        return int(index)
    else:
        matchs = re.findall("[0-9]+",index)
        if(len(matchs) > 0):
            matchVal = 0
            for m in matchs:
                matchVal *= 10
                matchVal += int(m)
            return matchVal+len(index)*10000
        else:
            return 10e+7

datas = {}
for dirPath,lan in AIM_DIRS.items():
    for f in os.listdir(dirPath):
        if os.path.isdir(os.path.join(dirPath,f))\
        or "test" in f \
        or "include" in f:
            continue
        t = Topic(dirPath,f)
        if t.index not in datas:
            datas[t.index] = []
        datas[t.index].append(t)
datas = list(datas.items())
datas.sort(key = indexCmp)

with open("README.md",'w+') as out:
    out.write("编号|标题|语言\n")
    out.write("---|---|---\n")
    for data in datas:
        indexStr = data[0]
        titleStr = data[1][0].title
        lanStr = ""
        for t in data[1]:
            lanStr += "[%s](%s),"%(t.language,t.path)
        res = ("%s|%s|%s\n")%(indexStr,titleStr,lanStr)
        out.write(res)


"""
File handler that downloads the files needed for program functionality
"""
import os.path
from shutil import which
import requests

class FileDownload():
    """
    This is a class that downloads files if they do not already exist in
    the supplied directory

    Attributes:
        model_url (str) : Static Google Drive url that hosts the .h5 file
        model_name (str) : Name of the model file being saved
        earth_url (str) : Static url that hosts the Google Earth deb file
        earth_name (str) : Name of the deb file to be saved
        current_directory (str) : Holds the string value of the current directory
        session : Instantiate Session class from requests
                response : Holds response object returned from HTTP request
        token (str) : Holds response token from Google Drive that is given when downloading
            large files
    """
    def __init__(self):
        """
        Please see help(FileDownload) for more info
        """
        self.model_url = 'https://drive.google.com/uc?export=download&id=18oXums8kjOF6iPw9iMwdXVlqqxswEiRm'
        self.model_name = '/pyearth_cnn_model_200612_1744.h5'
        self.earth_url = 'https://dl.google.com/dl/earth/client/current/google-earth-stable_current_amd64.deb'
        self.earth_name = '/google-earth-stable_current_amd64.deb'
        self.current_directory = ''
        self.session = requests.Session()
        self.response = None
        self.token = None
        self.chunk_size = 32768

    def get_drive_file(self):
        """
        Checks if model already exists in current directory. If it does not exist, downloads the
        model to the current directory.
        """
        # Gets current working directory and appends the target file name to it
        self.current_directory = os.getcwd() + self.model_name
        # Checks if the model file already exists in the current directory, returns if so
        if os.path.isfile(self.current_directory):
            return
        # Sends get request to Google Drive and stores response object
        self.response = self.session.get(self.model_url, stream=True)
        # If the file is large, a download warning is issued by Google Drive. Gets token
        # from this warning and stores it.
        for key, value in self.response.cookies.items():
            if key.startswith('download_warning'):
                token = value
                break
        # If the token exists, uses token to confirm large download.
        if token:
            params = {'confirm' : token}
            self.response = self.session.get(self.model_url, params=params, stream=True)
        # Opens current directory and saves model to it
        with open(self.current_directory, "wb") as my_file:
            for chunk in self.response.iter_content(self.chunk_size):
                if chunk:
                    my_file.write(chunk)

    def get_google_earth(self):
        """
        Checks if Google Earth is already installed. If not, creates google-earth folder
        in current directory and downloads the Google Earth deb to the new folder.

        Warning: If google-earth folder is created and file is downloaded there already,
        this will not install Google Earth. Please manually install using
        sudo dpkg -i google-earth-stable*.deb inside the relevant folder.
        """
        # Uses shutil which to determine if Google Earth is already installed
        if which('google-earth-pro') is not None:
            return
        # Creates path for new google-earth folder in current working directory
        self.current_directory = os.getcwd() + '/google-earth'
        # If google-earth folder does not exist, create it
        if not os.path.exists(self.current_directory):
            os.mkdir(self.current_directory)

        self.current_directory += self.earth_name
        # If Google Earth deb file is already downloaded in google-earth folder, return
        if os.path.isfile(self.current_directory):
            return
        # Sends get request and stores response object
        self.response = requests.get(self.earth_url)
        # Saves Google Earth deb file to current working directory/google-earth
        with open(self.current_directory, "wb") as my_file:
            my_file.write(self.response.content)
        # Google Earth deb has been downloaded if here, call function to install it
        self.install_google_earth()

    def install_google_earth(self):
        """
        Changes directory to google-earth folder, installs Google Earth deb, and
        returns to previous directory.
        """
        # Get current directory to return to
        self.current_directory = os.getcwd()
        # Change to google-earth folder
        os.chdir('google-earth')
        # Use dpkg to install Google Earth
        os.system('sudo dpkg -i google-earth-stable*.deb')
        # Return to previous directory
        os.chdir(self.current_directory)


# -*- coding: utf-8 -*-
import scrapy
from proxy.items import ProxyItem

class DxdlspiderSpider(scrapy.Spider):
    name = 'dxdlspider'
    allowed_domains = ['xicidaili.com']
    start_urls = []

    for i in range(1, 10):
        start_urls.append('https://www.xicidaili.com/wt/' + str(i))

    def parse(self, response):
        item = ProxyItem()
        # mian = response.xpath('//tr')

        # for li in mian:
        #     # 找到ip地址
        #     ip = li.xpath('/td').extract()[1]
        #     # 找到端口：
        #     port = li.xpath('/td/text()').extract()[2]
        #     # 将两者连接，并返回给item处理
        #     item['addr'] = ip + ':' + port
        #     yield item

        ip_list = response.xpath('//tr/td[2]/text()').extract()
        port_list = response.xpath('//tr/td[3]/text()').extract()

        temp = []
        for i in range(0, len(ip_list)):
            # temp.append(ip_list[i] + ':' + port_list[i])
            item['addr'] = ip_list[i] +':'+ port_list[i]
            yield item


# coding=utf-8
# Copyright 2021 DeepMind Technologies Limited..
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Common logging scheduling strategies."""

from typing import Callable, List, Union

from envlogger import step_data
import numpy as np

# A Scheduler returns True when something should be activated and False
# otherwise.
Scheduler = Callable[[step_data.StepData], bool]

def n_step_scheduler(step_interval: int) -> Scheduler:
  """Returns a closure that returns True every N times it is called.

  Args:
    step_interval: Must be a positive integer.
  Raises:
    ValueError: When `step_interval` is not a positive integer.
  """
  if step_interval <= 0:
    raise ValueError(f'step_interval must be positive, got {step_interval}')

  def f(unused_data: step_data.StepData) -> bool:
    should_log = f.step_counter % step_interval == 0
    f.step_counter += 1
    return should_log

  f.step_counter = 0
  return f

def bernoulli_step_scheduler(keep_probability: float) -> Scheduler:
  """Returns a closure that returns True with probability `keep_probability`.

  Args:
    keep_probability: Must be within [0,1].
  Raises:
    ValueError: When `keep_probability` is not in [0,1].
  """
  if keep_probability < 0.0 or keep_probability > 1.0:
    raise ValueError(
        f'keep_probability must be in [0,1], got: {keep_probability}')

  def f(unused_data: step_data.StepData) -> bool:
    return np.random.random() < keep_probability

  return f

def n_episode_scheduler(episode_interval: int) -> Scheduler:
  """Returns a closure that returns True every N episodes.

  Args:
    episode_interval: Must be a positive integer.
  Raises:
    ValueError: When `episode_interval` is not a positive integer.
  """
  if episode_interval <= 0:
    raise ValueError(
        f'episode_interval must be positive, got {episode_interval}')

  def f(data: step_data.StepData) -> bool:
    should_log = f.episode_counter % episode_interval == 0
    if data.timestep.last():
      f.episode_counter += 1
    return should_log

  f.episode_counter = 0
  return f

def bernoulli_episode_scheduler(keep_probability: float) -> Scheduler:
  """Returns a closure that keeps episodes with probability `keep_probability`.

  Args:
    keep_probability: Must be within [0,1].
  Raises:
    ValueError: When `keep_probability` is not in [0,1].
  """
  if keep_probability < 0.0 or keep_probability > 1.0:
    raise ValueError(
        f'keep_probability must be in [0,1], got: {keep_probability}')

  def f(data: step_data.StepData) -> bool:
    if data.timestep.last():
      f.current_p = np.random.random()
    return f.current_p < keep_probability

  f.current_p = np.random.random()

  return f

def list_step_scheduler(
    desired_steps: Union[List[int], np.ndarray]) -> Scheduler:
  """Returns a closure that keeps steps in `desired_steps`.

  Please see unit tests for examples of using this scheduler. In particular,
  you can use Numpy's functions such as logspace() to generate non-linear steps.

  Args:
    desired_steps: Step indices (0-based) that indicate desired steps to log.
  Raises:
    ValueError: When `desired_steps` is empty.
  """
  if (isinstance(desired_steps, np.ndarray) and
      not (desired_steps.dtype == np.int32 or desired_steps.dtype == np.int64)):
    raise TypeError(
        f'desired_steps.dtype must be np.in32 or np.int64: {desired_steps} '
        f'(dtype: {desired_steps.dtype})')
  if len(desired_steps) <= 0:
    raise ValueError(f'desired_steps cannot be empty: {desired_steps}')

  def f(unused_data: step_data.StepData) -> bool:
    should_log = f.step_counter in f.desired_steps
    f.step_counter += 1
    return should_log

  f.desired_steps = set(desired_steps)
  f.step_counter = 0
  return f

def list_episode_scheduler(
    desired_episodes: Union[List[int], np.ndarray]) -> Scheduler:
  """Returns a closure that keeps episodes in `desired_episodes`.

  Please see unit tests for examples of using this scheduler. In particular,
  you can use Numpy's functions such as logspace() to generate non-linear
  episodes.

  Args:
    desired_episodes: Episode indices (0-based) that indicate desired espiodes
        to log.
  Raises:
    ValueError: When `desired_episodes` is empty.
  """
  if (isinstance(desired_episodes, np.ndarray) and
      not (desired_episodes.dtype == np.int32 or
           desired_episodes.dtype == np.int64)):
    raise TypeError('desired_episodes.dtype must be np.in32 or np.int64: '
                    f'{desired_episodes} (dtype: {desired_episodes.dtype})')
  if len(desired_episodes) <= 0:
    raise ValueError(f'desired_episodes cannot be empty: {desired_episodes}')

  def f(data: step_data.StepData) -> bool:
    should_log = f.episode_counter in f.desired_episodes
    if data.timestep.last():
      f.episode_counter += 1
    return should_log

  f.desired_episodes = set(desired_episodes)
  f.episode_counter = 0
  return f


"""
Object representation for a graph
"""

# define a class that represents a graph node
class GraphNode:
    def __init__(self, value=None):
        self.value = value
        self.connections = []

g1 = GraphNode()
g2 = GraphNode()
g3 = GraphNode()
g4 = GraphNode()

# make connections from g1 to all other nodes without an add method
# this will make a directed graph because those nodes don't link back
# to g1
g1.connections.append(g2)
g1.connections.append(g3)
g1.connections.append(g4)

# Most common graph representation is an adjacency list
# each index in the list stores another list
# used to represent a directed graph
directed_graph = [
    [1, 2], # Node/Index 0, 0 connects to 1 and 2
    [3], # 1 connects to 3
    [3], # 2 connects to 3
    [4], # 3 connects to 4
    [], # 4 connects to nothing so insert an empty list
]

# Write a function to print out each graph
# node and all of the graph node's connections
def print_graph(graph):
    # Input: graph represented as an adjacency list
    # Output: prints out each graph node with its connections
    # iterate with a for loop
    # since we want access to both the index as well as the
    # value at that index in the adjacency list, use `enumerate`
    for node, connections in enumerate(graph): # node is the index which holds a list of connections
        print(f'Graph Node {node} is connected to {connections}')
print(print_graph(directed_graph))

# for an undirected graph
# use an adjacency matrix
undirected_graph = [
    []
]


#!/usr/bin/env python
# coding: utf-8
'''
Project Name: EP_WC0100
CTM Name    :
Crawl Cycle : Weekly
Main Website: https://business.591.com.tw
Description : RPA爬網需求：定期蒐尋仲介網站出售案件
Author      : 張至偉
Update Date : 2019.03.22

[20190402] 問題單:20190328131036
1. 調整輸出檔案的欄位順序
2. 新增查詢縣市
3. 新增"坪數"的檢核條件
'''

# In[1]:

import header

import os
import re
import datetime
import logging
import traceback

import requests
import pandas as pd
from bs4 import BeautifulSoup

TODAY = str(datetime.date.today())

WEB_URL = "https://business.591.com.tw"

# In[2]:

def getBlockInfo(itemSoup, patternKey, patternValue):

    # 使用 CSS 選擇器，選取指定區塊
    # 根據區塊處理，並回傳 dict 的查詢格式 {"標題":"內容"}
    rtnMap = {}
    keyList = itemSoup.select(patternKey)
    valueList = itemSoup.select(patternValue)

    for i, key in enumerate(keyList):
        rtnMap[key.text] = valueList[i].text

    return rtnMap

# In[3]:

def selectValueByPattern(soup, patternStr, index, removeChild=False):

    selectedList = soup.select(patternStr)

    # 根據 CSS 選擇器，選擇欲爬取的內容文字；若無文字顯示 "None"
    if len(selectedList) > index:

        # 是否移除子元素
        if removeChild:
            for childElement in selectedList[index].find_all():
                childElement.decompose()

        fieldValue = selectedList[index].text.strip()
    else:
        fieldValue = "None"

    # 以空白取代換行
    fieldValue = ' '.join(fieldValue.splitlines())

    return fieldValue

# In[4]:

def removeWord(inputString, removePatterns):

    rtnString = inputString
    for pString in removePatterns:
        pattern = re.compile("(\s*)" + pString + "(\s*)")
        rtnString = pattern.sub('', rtnString)

    return rtnString

# In[5]:

def getItemInfo(itemUrl):

    itemSoup = request2soup(itemUrl)
    addrInfo = getBlockInfo(itemSoup, "span.info-addr-key", "span.info-addr-value")
    floorInfo = getBlockInfo(itemSoup, "div.info-floor-value", "div.info-floor-key")

    # [20190402] 檢核坪數是否大於 300 坪
    # [20190408] 檢核變數是否為 "None"
    floorArea = removeWord(floorInfo.get("基地面積", "None"), ["坪"])
    if floorArea=="None" or float(floorArea) < 300:
        return None

    rtnMap = {}

    # [20190402] 調整輸出檔案的欄位順序
    rtnMap["縣市"] = selectValueByPattern(itemSoup, "div.breadList > a", 2)
    rtnMap["區域"] = selectValueByPattern(itemSoup, "div.breadList > a", 3)
    rtnMap["地址"] = addrInfo.get("地址", "None")
    rtnMap["土地類別"] = floorInfo.get("土地類別", "None")
    rtnMap["現況"] = addrInfo.get("現況", "None")
    rtnMap["總價(萬元)"] = selectValueByPattern(itemSoup, "span.info-price-num", 0, removeChild=True)
    rtnMap["基地面積(坪)"] = floorArea
    rtnMap["單價(萬/坪)"] = removeWord(selectValueByPattern(itemSoup, "div.info-price-per", 0), ["單價：", "萬/坪房貸試算"])
    rtnMap["標題"] = selectValueByPattern(itemSoup, "h1.detail-title-content", 0)
    rtnMap["待售物件連結"] = itemUrl
    rtnMap["適合用途"] = addrInfo.get("適合用途", "None")
    rtnMap["有效期"] = removeWord(selectValueByPattern(itemSoup, "span.detail-info-span.pull-right", 0), ["有效期："])
    rtnMap["仲介"] = selectValueByPattern(itemSoup, "div.info-host-name > span.info-span-name", 0)
    rtnMap["經紀業"] = selectValueByPattern(itemSoup, "div.info-host-detail > span", 0)
    rtnMap["電話"] = selectValueByPattern(itemSoup, "span.info-host-word", 0)
    rtnMap["屋況特色"] = selectValueByPattern(itemSoup, "div.detail-feature-text", 0)
    rtnMap["編號"] = selectValueByPattern(itemSoup, "span.breadList-last", 0)

    return rtnMap

# In[6]:

def parsingDetail(standbyDataFrame):

    itemUrlList = list(standbyDataFrame["link"])

    # [20190402] 調整輸出檔案的欄位順序
    detailDataFrame = pd.DataFrame(columns=["縣市", "區域", "地址", "土地類別", "現況", "總價(萬元)", "基地面積(坪)", "單價(萬/坪)",
    "標題", "待售物件連結","適合用途", "有效期", "仲介", "經紀業", "電話", "屋況特色", "編號"])

    for itemUrl in itemUrlList:
        try:
            itemInfo = getItemInfo(itemUrl)
            detailDataFrame = detailDataFrame.append(itemInfo, ignore_index=True)
        except:
            # 移除未成功的連結
            standbyDataFrame = standbyDataFrame[standbyDataFrame["link"] != itemUrl]
            setErrorMessage("爬取商品內文失敗，失敗連結：" + itemUrl, setEXIT_CODE=False)

    return standbyDataFrame, detailDataFrame

# In[7]:

def parsingTitle(reqMap):

    try:
        # 取得上次爬網結果
        historyFile = os.path.join(header.LAST_RESULT_PATH, "crawlHistory.csv")
        if os.path.isfile(historyFile):
            historyDataFrame = pd.read_csv(historyFile)
            historyList = list(historyDataFrame["link"])
        else:
            historyDataFrame = None
            historyList = []

        # 爬網日期, 連結, 標題
        standbyDataFrame = pd.DataFrame(columns=["date", "link", "title"])

        queryParams = reqMap["CONDITIONS"]

        # 設定查詢地區
        for regionId in reqMap["REGION_IDs"]:

            # 設定查詢條件
            for queryType in reqMap["QUERY_TYPEs"]:

                queryParams["land"] = queryType
                queryParams["firstRow"] = 0

                # 確認總筆數
                tmpSoup = request2soup(WEB_URL, regionId, queryParams)
                totalNumberPattern = tmpSoup.select("div.pull-left.hasData > i")
                totalNumber = int(totalNumberPattern[0].text) if len(totalNumberPattern) > 0 else 0

                # 查無資料，執行下一個查詢條件
                if totalNumber < 1:
                    continue

                try:
                    # 逐頁處理，每頁30筆
                    for itemNo in range(0, totalNumber, 30):

                        # 設定查詢頁數
                        queryParams["firstRow"] = itemNo

                        # 取得商品列表
                        rtnListSoup = request2soup(WEB_URL, regionId, queryParams)
                        items = rtnListSoup.select("li.infoContent > h3 > a")

                        # 過濾已爬取的物品，並記錄此次待查清單
                        for item in items:
                            url = "https:" + ' '.join(item.get("href").split())
                            if url not in historyList:
                                dataInfo = {"date":TODAY, "link":url, "title":item.text}
                                standbyDataFrame = standbyDataFrame.append(dataInfo, ignore_index=True)

                except:
                    errMsg = "爬取區域：%s 查詢條件：%s 第 %s 頁商品列表發生錯誤"\
                        % (reqMap["REGION_IDs"][region_id], reqMap["QUERY_TYPEs"][query_type], str(int(page_num/30)+1))
                    setErrorMessage(errMsg, setEXIT_CODE=False)

        return standbyDataFrame, historyDataFrame

    except:
        setErrorMessage("爬取商品列表失敗")
        return pd.DataFrame(columns=["date", "link", "title"]), None

# In[8]:

def request2soup(url, regionId=None, conditions=None):

    HEADERS = {
        "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 Safari/537.36",
        "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
        "accept-encoding": "gzip, deflate, br",
        "accept-language": "en-US,en;q=0.9",
        "connection": "keep-alive",
        "dnt": "1"
    }

    # 設定查詢地區
    regionCookie = None if regionId == None else {"urlJumpIp": regionId}

    res = requests.get(url, headers=HEADERS, cookies=regionCookie, params=conditions)
    res.encoding = "utf-8"

    return BeautifulSoup(res.text, "html.parser")

# In[9]:

def setErrorMessage(errorMsg, setEXIT_CODE=True):

    if setEXIT_CODE:
        header.EXIT_CODE = -1

    print(errorMsg)
    logging.error(errorMsg)
    traceback.print_exc()

# In[10]:

def main():
    header.processBegin(url=WEB_URL)
    header.clearFolder()

    try:
        reqMap = {
            # [20190402] 新增查詢的縣市
            "REGION_IDs": {
                "1" : "台北市", "2" : "基隆市", "3" : "新北市", "4" : "新竹市", "5" : "新竹縣", "6" : "桃園市",
                "7" : "苗栗縣", "8" : "台中市", "10": "彰化縣", "11": "南投縣", "12": "嘉義市", "13": "嘉義縣",
                "14": "雲林縣", "15": "台南市", "17": "高雄市", "19": "屏東縣", "21": "宜蘭縣", "22": "台東縣",
                "23": "花蓮縣", "24": "澎湖縣", "25": "金門縣", "26": "連江縣"
            },
            "QUERY_TYPEs": {'1':"住宅用地", '2':"商業用地", '3':"工業用地"},
            "CONDITIONS": {
                "is_new_list": '1',
                "type": '2',
                "searchtype": '1',
                "firstRow": '0',
                "kind": "11", # 廠房土地出售
                "area": "300,"# 最小 300 坪
            }
        }

        standbyDataFrame, historyDataFrame = parsingTitle(reqMap)

        if len(standbyDataFrame) < 1:
            # 無資料更新
            logMsg = "無資料更新，爬網日期：" + TODAY
            print(logMsg)
            logging.critical(logMsg)
        else:
            finishDataFrame, detailDataFrame = parsingDetail(standbyDataFrame)

            header.outputCsv(detailDataFrame, header.PROJECT)
            header.RESULT_COUNT = len(detailDataFrame)

            # 更新 crawlHistory 檔案
            updateHistoryDataFrame = pd.concat([historyDataFrame, finishDataFrame], ignore_index=True)
            header.outputCsv(updateHistoryDataFrame, "crawlHistory", header.LAST_RESULT_PATH)

        header.zipFile()
        header.createInfoFile()
        header.createOKFile()

    except:
        setErrorMessage("執行爬網作業失敗")
        header.createInfoFile()
        header.zipFile(zipFolder=header.LOG_PATH, zipResultWithLog=False)

    header.processEnd()

# In[11]:

if __name__ == "__main__":
    print(header.TIMELABEL)
    logging.fatal("FINAL_PATH:" + header.FINAL_PATH)
    main()


from django.db import models

class Country(models.Model):
    name = models.CharField(max_length=100)
    h1 = models.CharField(max_length=200, default='')
    words = models.CharField(max_length=255, default=None, blank=True)
    first = models.TextField(default='')
    second = models.TextField(default='')
    description = models.TextField(default='')
    def __unicode__(self):
        return self.name

class Collection(models.Model):
    name = models.CharField(max_length=100)
    image = models.ImageField(upload_to='uploads/', default='uploads/no_image.jpg')
    text = models.TextField(max_length=1024, default='')
    sq_meter_cost = models.IntegerField(default=0)

    def __unicode__(self):
        return self.name

class Size(models.Model):
    width = models.FloatField(default=0)
    length = models.FloatField(default=0)

    def __unicode__(self):
        return str(self.width) + " x " + str(self.length)

    def square(self):
        return self.width * self.length

class Shape(models.Model):
    name = models.CharField(max_length=100)
    h1 = models.CharField(max_length=255, default=None)
    words = models.CharField(max_length=255, default=None, blank=True)
    first = models.TextField(default='')
    second = models.TextField(default='')
    description = models.TextField(default='')

    def __unicode__(self):
        return self.name

class Color(models.Model):
    name = models.CharField(max_length=100)

    def __unicode__(self):
        return self.name

class Picture(models.Model):
    name = models.CharField(max_length=100)

    def __unicode__(self):
        return self.name

class Carpet(models.Model):
    name = models.CharField(max_length=100)
    density = models.IntegerField(default=520000)
    height = models.IntegerField(default=8)
    text = models.TextField()
    image = models.ImageField(upload_to='uploads/', default='uploads/no_image.jpg')
    color = models.ForeignKey(Color)
    collection = models.ForeignKey(Collection)
    country = models.ForeignKey(Country)
    sizes = models.ManyToManyField(Size)
    shape = models.ForeignKey(Shape)
    picture = models.ForeignKey(Picture)
    is_children = models.BooleanField(default=0, null=False)

    def __unicode__(self):
        return self.name

    def have_discounts(self):
        if self.discounts.count() > 0:
            return True
        return False

class Product(models.Model):
    carpet = models.ForeignKey(Carpet)
    size = models.ForeignKey(Size)

    @property
    def price(self):
        try:
            discount = Discount.objects.get(carpet=self.carpet, size=self.size)
            return discount.price
        except:
            cost = self.carpet.collection.sq_meter_cost * self.size.square()
            cost /= 100
            cost = round(cost)*100
            cost = int(cost)
            return cost

class Order(models.Model):
    products = models.ManyToManyField(Product, unique=False)
    date = models.DateTimeField(auto_now_add=True)
    name = models.CharField(max_length=100)
    phone = models.CharField(max_length=20)
    email = models.CharField(max_length=100)
    adress = models.CharField(max_length=200)

class Discount(models.Model):
    carpet = models.ForeignKey(Carpet, related_name='discounts')
    size = models.ForeignKey(Size)
    price = models.IntegerField(null=True, default=None)

    def old_price(self):
        cost = self.carpet.collection.sq_meter_cost * self.size.square()
        cost /= 100
        cost = round(cost)*100
        cost = int(cost)
        return cost

    def percent(self):
        cost = self.old_price()
        percent = float(cost - self.price)*100/cost
        percent = round(percent)
        percent = int(percent)
        return percent

class News(models.Model):
    title = models.CharField(max_length=255)
    text = models.TextField()
    date = models.DateTimeField()

    def __unicode__(self):
        return self.title


"""
Compute some statistics of the dataset:
    *Number of species
    *Number of images
    *The species with the maximal/minimal number of images
    *Average/Std number of images per species
"""

import numpy as np
from scipy import misc
import os
from tqdm import tqdm

dataset_summary = 'dataset/dataset_summary.txt'
#Read summary
with open(dataset_summary, 'r') as f:
    content = f.read().split('\n')[1:-1]
content = [c.split('\t') for c in content]
#Get species
species = set([c[3] for c in content])
print('number of species: {}'.format(len(species)))
#Get number of images
data = {s:[] for s in species}
for c in content:
    data[c[3]].append(c[0])
data = {s:len(data[s]) for s in data}
nb_images = [data[s] for s in sorted(data.keys())]
#Get stats species
avg = np.array(nb_images).mean()
std = np.array(nb_images).std()
total_images = sum(nb_images)
max_indiv = 0
max_species = ''
min_indiv = float('inf')
min_species = ''
for s in data:
    if data[s] > max_indiv:
        max_species = s
        max_indiv = data[s]
    elif data[s] < min_indiv:
        min_species = s
        min_indiv = data[s]
print('min species: {} ({} images), max species: {} ({} images)'.format(min_species, min_indiv, max_species, max_indiv))
print('total images: {} (avg./species: {}, std./species: {})'.format(total_images, avg, std))
#Get stats images
# max_h = 0
# min_h = float('inf')
# max_w = 0
# min_w = float('inf')
# height = []
# width = []
# for c in content:
#     img = misc.imread(os.path.join('dataset', c[1]))
#     shape = img.shape
#     if shape[0] > max_h:
#         max_h = shape[0]
#     if shape[0] < min_h:
#         min_h = shape[0]
#     if shape[1] > max_w:
#         max_w = shape[1]
#     if shape[1] < min_w:
#         min_w = shape[1]
#     height.append(shape[0])
#     width.append(shape[1])
# height = np.array(height)
# width = np.array(width)
# mean_h = height.mean()
# std_h = height.std()
# mean_w = width.mean()
# std_w = width.std()
max_h = 800
min_h = 194
max_w = 800
min_w = 210
mean_h = 696.6331238255685
std_h = 77.23647074292813
mean_w = 785.7673491868075
std_w = 50.05793388772877
print('min height: {}, max height: {} (mean: {}, std: {}), min width: {}, max width: {} (mean: {}, std: {})'.format(
                            min_h, max_h, mean_h, std_h, min_w, max_w, mean_w, std_w))
height = int(mean_h)
width = int(mean_w)
red = np.zeros((height, width))
green = np.zeros((height, width))
blue = np.zeros((height, width))
for c in tqdm(content):
    img = misc.imresize(misc.imread(os.path.join('dataset', c[1])), (height, width)) / 255
    img = img.transpose((2, 0, 1))
    red += img[0]
    green += img[1]
    blue += img[2]
red /= len(content)
green /= len(content)
blue /= len(content)
mean = [red.mean(), green.mean(), blue.mean()]
var = [red.var(), green.var(), blue.var()]
# mean = [0.67152, 0.69242, 0.67371]
# var = [0.02041, 0.02109, 0.02269]
print('mean pixel: {} (var: {})'.format(mean, var))


import logging

from config import config_logging, SERVER_PORT, WEB_PANEL_PORT, \
    POWER_OFF_IF_OFFLINE, POWER_OFF_OUT_OF_PLAN
from framework import CMPServer, CMPRequestHandler
from models import PowerPlan, Room, StudentComputer
from task import PowerPlanScheduler
from web import web_app

class ComputerServer(CMPServer):
    def __init__(self, port):
        CMPServer.__init__(self, port)
        self.set_request_handlers({
            'hello': HelloHandler(),
            'heart': HeartHandler()
        })

def make_config_response():
    return {
        'status': 'success',
        'power_off_if_offline': POWER_OFF_IF_OFFLINE
    }

class HelloHandler(CMPRequestHandler):
    def __init__(self):
        CMPRequestHandler.__init__(self)

    @staticmethod
    def make_power_off_request():
        return {
            'action': 'power',
            'plan': PowerPlan.PLAN_POWER_OFF,
            'time': 1
        }

    def handle_request(self, request, sock):
        logging.info('Hello request form: %s' % request.get('mac'))
        request.write_response(make_config_response())
        room = Room.from_address(request.get_address())
        if room is None:
            return
        computer = StudentComputer.register_computer(request)
        plan = PowerPlan.get_coming_power_plan(computer)
        if computer is not None and plan is not None:
            plan.send_to_computer(computer)
        elif plan is None and computer is not None:
            if POWER_OFF_OUT_OF_PLAN:
                computer.send_request(HelloHandler.make_power_off_request())

class HeartHandler(CMPRequestHandler):
    def __init__(self):
        CMPRequestHandler.__init__(self)
        self._delay = 20000

    def make_ok_response(self):
        return {
            'status': 200,
            'delay': self._delay
        }

    def handle_request(self, request, sock):
        request.write_response(self.make_ok_response())

if __name__ == '__main__':
    config_logging()

    server = ComputerServer(SERVER_PORT)
    server.run_in_background()

    power_scheduler = PowerPlanScheduler()
    power_scheduler.start()

    web_app.run(debug=False, port=WEB_PANEL_PORT)


import os
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'
import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()

x_data = [[1, 2, 1, 1],
          [2, 1, 3, 2],
          [3, 1, 3, 4],
          [4, 1, 5, 5],
          [1, 7, 5, 5],
          [1, 2, 5, 6],
          [1, 6, 6, 6],
          [1, 7, 7, 7]]
y_data = [[0, 0, 1],
          [0, 0, 1],
          [0, 0, 1],
          [0, 1, 0],
          [0, 1, 0],
          [0, 1, 0],
          [1, 0, 0],
          [1, 0, 0]]

X = tf.placeholder("float" , [None,4])
Y = tf.placeholder("float", [None,3])
nb_classes = 3

W=tf.Variable(tf.random_normal([4,nb_classes]) , name = 'weight')
b=tf.Variable(tf.random_normal([nb_classes]) , name='bias')

#tf.nn.softmax computes softmax activations
#softmax = exp(Logits) / reduce_sum(exp(Logits) , dim )
hypothesis = tf.nn.softmax(tf.matmul(X,W)+b)

#cross entropy cost/loss
cost= tf.reduce_mean(-tf.reduce_sum(Y*tf.log(hypothesis) , axis =1))
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.1).minimize(cost)

with tf.Session() as sess :
    sess.run(tf.global_variables_initializer())

    a = sess.run(hypothesis, feed_dict={X:[[1,11,7,9] ,[1,3,4,3] , [1,1,0,1] ]})
    print(a , sess.run(tf.arg_max(a,1)))


class Solution(object):
    def isAnagram(self, s, t):
        if len(s) != len(t):
            return False
        charCount = {}
        for idx in range(len(s)):
            if s[idx] not in charCount:
                charCount[s[idx]] = 0
            if t[idx] not in charCount:
                charCount[t[idx]] = 0
            charCount[s[idx]] += 1
            charCount[t[idx]] -= 1
        for char in charCount:
            if charCount[char] != 0:
                return False
        return True


from typing import List

from ..core.base import FeatureExtractor
import pandas as pd
import numpy as np
import logging

class ColorFeatureExtractor(FeatureExtractor):
    def get_features_keys(self) -> List[str]:
        return ['g-r_max', 'g-r_mean', 'g-r_max_corr', 'g-r_mean_corr']

    def get_required_keys(self) -> List[str]:
        return ['fid', 'magpsf', 'magpsf_ml']

    def _compute_features(self, detections, **kwargs):
        """
        Parameters
        ----------
        detections :class:pandas.`DataFrame`
        DataFrame with detections of an object.
        kwargs Not required.
        Returns :class:pandas.`DataFrame`
        -------
        """
        # pd.options.display.precision = 10
        oids = detections.index.unique()
        colors = []
        for oid in oids:
            oid_detections = detections.loc[[oid]]
            if 1 not in oid_detections.fid.unique() or 2 not in oid_detections.fid.unique():
                logging.info(
                    f'extractor=COLOR  object={oid}  required_cols={self.get_required_keys()}  filters_qty=2')
                colors.append(self.nan_df(oid))
                continue

            g_band_mag_corr = oid_detections[oid_detections.fid == 1]['magpsf_ml'].values
            r_band_mag_corr = oid_detections[oid_detections.fid == 2]['magpsf_ml'].values

            g_band_mag = oid_detections[oid_detections.fid == 1]['magpsf'].values
            r_band_mag = oid_detections[oid_detections.fid == 2]['magpsf'].values

            g_r_max = g_band_mag.min() - r_band_mag.min()
            g_r_mean = g_band_mag.mean() - r_band_mag.mean()

            g_r_max_corr = g_band_mag_corr.min() - r_band_mag_corr.min()
            g_r_mean_corr = g_band_mag_corr.mean() - r_band_mag_corr.mean()

            if g_r_max == g_r_max_corr and g_r_mean == g_r_mean_corr:
                data = [g_r_max, g_r_mean, np.nan, np.nan]
            else:
                data = [g_r_max, g_r_mean, g_r_max_corr, g_r_mean_corr]
            oid_color = pd.DataFrame([data], columns=self.get_features_keys(), index=[oid])
            colors.append(oid_color)
        colors = pd.concat(colors, axis=0)
        colors.index.name = 'oid'
        return colors


import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_excel('car.xlsx')
df1=df.head(10)

plt.rcParams['font.sans-serif']=['SimHei'] #解决中文乱码
x=df1['车型']
y=df1['1月销量']
#plt.grid(axis='y')
#调整图表距左的空白
plt.subplots_adjust(left=0.2)
#4个方向的坐标轴上的刻度线是否显示
plt.tick_params(bottom=False,left=False)
# 添加刻度标签
plt.yticks(range(10))
#图表标题
plt.title('2020年1月国产品牌汽车销量TOP10')
plt.barh(x, y,color='Turquoise')  #柱子蓝绿色
plt.show()


from keras.preprocessing.image import ImageDataGenerator
from keras.applications.inception_v3 import InceptionV3
from keras.models import Model
from keras.layers import Dense, GlobalAveragePooling2D

def img_gen(path='data'):
    train_datagen = ImageDataGenerator(
        rescale=1./255)

    train_generate = train_datagen.flow_from_directory(
        path,
        target_size=(255, 255),
        batch_size=32,
        class_mode='categorical')

    return train_generate

if __name__ == "__main__":
    base_model = InceptionV3(include_top=False)

    x = base_model.output
    x = GlobalAveragePooling2D()(x)

    predictions = Dense(5749, activation='softmax')(x)

    model = Model(input=base_model.input, output=predictions)

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

    print("Compiled. Preparing Images...")
    train_gen = img_gen()
    print("Fitting Model")
    model.fit_generator(
        train_gen,
        samples_per_epoch=5749,
        nb_epoch=50)

    model.save('face.h5')


from django.core.management.base import BaseCommand, CommandError
from algos import *
import pickle
import sys
import numpy as np

sys.path.append('../convnet/')
# from PascalNet import PascalNet
from skimage.viewer import ImageViewer
from skimage.io import imread
from skimage.transform import resize

from CBIR.models import Image

class Command(BaseCommand):

    def add_arguments(self, parser):

        parser.add_argument('--algo')

    def handle(self, *args, **options):

        if options['algo'] == 'bag':

            bag_of_words()

    def two_labels_data(self):

        print('Loading datasets...')

        test_files = np.loadtxt('/Users/leo/Documents/Projets/image_retrieval/datasets/Pascal/'+'VOC2012_test/ImageSets/Main/test.txt', dtype=bytes).astype(str)
        test_files = test_files[0:100]

        test_set_x = None
        for test_file in test_files:

            img = imread('/Users/leo/Documents/Projets/image_retrieval/datasets/Pascal/'+'VOC2012_test/JPEGImages/'+test_file+'.jpg')

            img = resize(img, (200,285,3))

            if test_set_x is None:
                test_set_x = np.concatenate(([img],))
            else:
                test_set_x = np.concatenate((test_set_x, [img]))

        test_set_x = np.array(test_set_x)
        print('size test set : %d' % len(test_set_x))

        test_set_x = np.transpose(test_set_x, (0,3,1,2))

        return test_set_x, test_files

    def convnet():

        convnet = pickle.load(open('../pascal/best_model.p', 'rb'))
        images, names = self.two_labels_data()

        features = convnet.get_features(images)

        for feature, name in zip(features, names):

            img = Image(
                        features=feature,
                        name = name
                        )
            img.save()


#!/usr/bin/python3

import os
import sys
import time

PID_FILE = '/var/run/system_hello.pid'

def main():
    while True:
        print("Hello world", file=sys.stdout, flush=True)
        print("Hello errors.", file=sys.stderr, flush=True)
        time.sleep(60 * 60);

if __name__ == '__main__':
    try:
        with open(PID_FILE, mode='w') as f:
            f.write(str(os.getpid()));
    except PermissionError:
        print("ERROR: This script must be run with root permissions",
                file=sys.stderr)
        sys.exit(1)

    main()


from OpenGL.GL import *
from OpenGL.GLU import *

#MODES
AMBIENT = GL_AMBIENT
DIFFUSE = GL_DIFFUSE
SPECULAR = GL_SPECULAR
SHININESS = GL_SHININESS
EMISSION = GL_EMISSION
AMBIENT_AND_DIFFUSE = GL_AMBIENT_AND_DIFFUSE

map_modes = [DIFFUSE, AMBIENT, SPECULAR, SHININESS, EMISSION]

#FACE
FRONT = GL_FRONT
BACK = GL_BACK
FRONT_BACK = GL_FRONT_AND_BACK

face_modes = [FRONT_BACK, FRONT_BACK, FRONT_BACK, FRONT_BACK, FRONT_BACK]

class Material:

    def __init__(self,
                 diffuse = (0.5, 0.5, 0.5, 1),
                 ambient = (0.5, 0.5, 0.5, 1),
                 specular = (0, 0, 0, 1),
                 shine = (128.0, ), emission = (10.0, )):

        self.Diffuse = diffuse
        self.Ambient = ambient
        self.Specular = specular
        self.Shine = shine
        self.Emission = emission

        self.Parameters = [self.Diffuse, self.Ambient, self.Specular, self.Shine, self.Emission]

    def setParam(self, param, value):

        #Ensure that the value is given in correct format
        if param.lower() == "shine" or param.lower() == "emission":
            if type(param) is not type(()) or len(param) is not 1:
                raise TypeError("Parameter 1 must be tuple of len 1: (x, )")
        else:
            if len(param) is not 4:
                raise IndexError("Parameter 1 must have a length of 4")

        if param.lower() == "diffuse":
            self.Diffuse = value
        elif param.lower() == "ambient":
            self.Ambient = value
        elif param.lower() == "specular":
            self.Specular = value
        elif param.lower() == "shine" or param.lower() == "shininess":
            self.Shine = value
        elif param.lower() == "emission":
            self.Emission = value
        else:
            raise ValueError("No matching call for Parameter 1")

    def getParam(self, param):

        if param.lower() == "diffuse":
            return self.Diffuse
        elif param.lower() == "ambient":
            return self.Ambient
        elif param.lower() == "specular":
            return self.Specular
        elif param.lower() == "shine" or param.lower() == "shininess":
            return self.Shine
        elif param.lower() == "emission":
            return self.Emission
        else:
            raise ValueError("No matching call for Parameter 1")

    def setFaceModes(self, param, face_mode):

        parameters = ["diffuse", "ambient", "specular", ["shine", "shininess"], "emission"]

        counter = 0
        for p in parameters:
            if type(p) == type(""):
                if param.lower() == p:
                    face_modes[counter] = face_mode
            else:
                if param.lower() == p[0] or param.lower() == p[1]:
                    face_modes[counter] = face_mode

            counter += 1

    def apply(self):

        for (fm, mm, param) in ([face_modes, map_modes, self.Parameters]):
            glMaterial(fm, mm, param)


# Game status categories
# Change the values as you see fit
STATUS_WIN = "win"
STATUS_LOSE = "lose"
STATUS_ONGOING = "ongoing"

class Hangman(object):
    def __init__(self, word):
        self.remaining_guesses = 9
        self.status = STATUS_ONGOING
        self.word = word
        self.letters_left_to_guess = set(word)

    def guess(self, char):
        self.validate_status()

        if char in self.letters_left_to_guess:
            self.letters_left_to_guess.remove(char)
        else:
            self.remaining_guesses -= 1

        self.update_status()

    def validate_status(self):
        if self.status == STATUS_LOSE:
            raise ValueError("No guesses remaining.")

        if self.status == STATUS_WIN:
            raise ValueError("You already won ya dingus!")

    def update_status(self):
        if len(self.letters_left_to_guess) == 0:
            self.status = STATUS_WIN
        elif self.remaining_guesses < 0:
            self.status = STATUS_LOSE

    def get_masked_word(self):
        return "".join(([c if c not in self.letters_left_to_guess else '_' for c in self.word]))

    def get_status(self):
        return self.status